From 3419a9dd542a1696fe67f0d0bf611b844629032d Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Fri, 15 Aug 2025 20:41:25 +0700 Subject: [PATCH 01/21] Cosmetic filters in a flatbuffer --- benches/bench_rules.rs | 2 +- benches/bench_serialization.rs | 12 +- examples/generate-dat.rs | 2 +- js/src/lib.rs | 2 +- src/blocker.rs | 11 +- src/cosmetic_filter_cache.rs | 564 ++-- src/data_format/mod.rs | 111 - src/data_format/storage.rs | 308 --- src/data_format/utils.rs | 32 - src/engine.rs | 51 +- src/filters/fb_builder.rs | 17 +- src/filters/fb_network.rs | 8 +- src/flatbuffers/containers/flat_map.rs | 69 + src/flatbuffers/containers/flat_multimap.rs | 3 + src/flatbuffers/containers/mod.rs | 1 + src/flatbuffers/fb_network_filter.fbs | 48 + .../fb_network_filter_generated.rs | 2352 ++++++++++------- src/lib.rs | 1 - src/network_filter_list.rs | 6 - src/resources/mod.rs | 4 + tests/legacy_harness.rs | 6 +- tests/live.rs | 4 +- tests/ublock-coverage.rs | 2 +- tests/unit/engine.rs | 22 +- 24 files changed, 1985 insertions(+), 1653 deletions(-) delete mode 100644 src/data_format/mod.rs delete mode 100644 src/data_format/storage.rs delete mode 100644 src/data_format/utils.rs create mode 100644 src/flatbuffers/containers/flat_map.rs diff --git a/benches/bench_rules.rs b/benches/bench_rules.rs index f5b850ef..4136bbde 100644 --- a/benches/bench_rules.rs +++ b/benches/bench_rules.rs @@ -100,7 +100,7 @@ fn blocker_new(c: &mut Criterion) { .collect(); let brave_list_rules: Vec<_> = rules_from_lists(&["data/brave/brave-main-list.txt"]).collect(); let engine = Engine::from_rules(&brave_list_rules, Default::default()); - let engine_serialized = engine.serialize().unwrap(); + let engine_serialized = engine.serialize().to_vec(); group.bench_function("el+ep", move |b| b.iter(|| get_engine(&easylist_rules))); group.bench_function("brave-list", move |b| { diff --git a/benches/bench_serialization.rs b/benches/bench_serialization.rs index 8076eb5f..ff84d17a 100644 --- a/benches/bench_serialization.rs +++ b/benches/bench_serialization.rs @@ -18,19 +18,19 @@ fn serialization(c: &mut Criterion) { ]); let engine = Engine::from_rules(full_rules, Default::default()); - b.iter(|| assert!(!engine.serialize().unwrap().is_empty())) + b.iter(|| assert!(!engine.serialize().to_vec().is_empty())) }); group.bench_function("el", move |b| { let full_rules = rules_from_lists(&["data/easylist.to/easylist/easylist.txt"]); let engine = Engine::from_rules(full_rules, Default::default()); - b.iter(|| assert!(!engine.serialize().unwrap().is_empty())) + b.iter(|| assert!(!engine.serialize().to_vec().is_empty())) }); group.bench_function("slimlist", move |b| { let full_rules = rules_from_lists(&["data/slim-list.txt"]); let engine = Engine::from_rules(full_rules, Default::default()); - b.iter(|| assert!(!engine.serialize().unwrap().is_empty())) + b.iter(|| assert!(!engine.serialize().to_vec().is_empty())) }); group.finish(); @@ -48,7 +48,7 @@ fn deserialization(c: &mut Criterion) { ]); let engine = Engine::from_rules(full_rules, Default::default()); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); b.iter(|| { let mut deserialized = Engine::default(); @@ -59,7 +59,7 @@ fn deserialization(c: &mut Criterion) { let full_rules = rules_from_lists(&["data/easylist.to/easylist/easylist.txt"]); let engine = Engine::from_rules(full_rules, Default::default()); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); b.iter(|| { let mut deserialized = Engine::default(); @@ -70,7 +70,7 @@ fn deserialization(c: &mut Criterion) { let full_rules = rules_from_lists(&["data/slim-list.txt"]); let engine = Engine::from_rules(full_rules, Default::default()); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); b.iter(|| { let mut deserialized = Engine::default(); diff --git a/examples/generate-dat.rs b/examples/generate-dat.rs index 86845c5f..513db3f7 100644 --- a/examples/generate-dat.rs +++ b/examples/generate-dat.rs @@ -21,7 +21,7 @@ fn main() { ) .unwrap(); assert!(engine.check_network_request(&request).exception.is_some()); - let serialized = engine.serialize().expect("Could not serialize!"); + let serialized = engine.serialize().to_vec(); // Write to file let mut file = File::create("engine.dat").expect("Could not create serialization file"); diff --git a/js/src/lib.rs b/js/src/lib.rs index 7396162a..ee8a7add 100644 --- a/js/src/lib.rs +++ b/js/src/lib.rs @@ -248,7 +248,7 @@ fn engine_url_cosmetic_resources(mut cx: FunctionContext) -> JsResult { fn engine_serialize(mut cx: FunctionContext) -> JsResult { let this = cx.argument::>(0)?; let serialized = if let Ok(engine) = this.0.lock() { - engine.serialize().unwrap() + engine.serialize().to_vec() } else { cx.throw_error("Failed to acquire lock on engine")? }; diff --git a/src/blocker.rs b/src/blocker.rs index 46f21df6..4952a477 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -440,9 +440,14 @@ impl Blocker { network_filters: Vec, options: &BlockerOptions, ) -> Self { - use crate::filters::{fb_builder::make_flatbuffer, fb_network::FilterDataContext}; - - let memory = make_flatbuffer(network_filters, options.enable_optimizations); + use crate::filters::{fb_builder::make_flatbuffer_from_rules, fb_network::FilterDataContext}; + + let memory = make_flatbuffer_from_rules( + network_filters, + vec![], // no cosmetic filters for blocker test + options.enable_optimizations, + 0, + ); let filter_data_context = FilterDataContext::new(memory); Self::from_context(filter_data_context) } diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index aef3bff7..0498f913 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -11,14 +11,50 @@ use crate::filters::cosmetic::{ CosmeticFilter, CosmeticFilterAction, CosmeticFilterMask, CosmeticFilterOperator, }; +use crate::filters::fb_network::flat::fb; +use crate::filters::fb_network::FilterDataContextRef; +use crate::flatbuffers::containers::flat_map::{FlatMapBuilder, FlatMapView}; +use crate::flatbuffers::containers::flat_multimap::{ + FlatMapStringView, FlatMultiMapBuilder, FlatMultiMapView, +}; + +use crate::flatbuffers::containers::flat_serialize::{ + serialize_vec_opt, FlatBuilder, FlatSerialize, +}; +use crate::flatbuffers::containers::flat_set::FlatSetView; + use crate::resources::{PermissionMask, ResourceStorage}; use crate::utils::Hash; use std::collections::{HashMap, HashSet}; +use flatbuffers::WIPOffset; use memchr::memchr as find_char; use serde::{Deserialize, Serialize}; +/// Encodes permission bits in the first byte of a script string +/// Returns the script with permission byte prepended +fn encode_script_with_permission(script: String, permission: PermissionMask) -> String { + let mut encoded = String::with_capacity(script.len() + 1); + encoded.push(permission.to_bits() as char); + encoded.push_str(&script); + encoded +} + +/// Decodes permission bits from the first byte of a script string +/// Returns (permission, script) tuple +fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { + if encoded_script.is_empty() { + return (PermissionMask::default(), encoded_script); + } + + let first_char = encoded_script.chars().next().unwrap(); + let permission_bits = first_char as u8; + let permission = PermissionMask::from_bits(permission_bits); + let script = &encoded_script[first_char.len_utf8()..]; + (permission, script) +} + /// Contains cosmetic filter information intended to be used on a particular URL. #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct UrlSpecificResources { @@ -63,49 +99,61 @@ impl UrlSpecificResources { /// will be blocked on any particular page, although when used correctly, all provided rules and /// scriptlets should be safe to apply. pub(crate) struct CosmeticFilterCache { - /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. - pub(crate) simple_class_rules: HashSet, - /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. - pub(crate) simple_id_rules: HashSet, - /// Rules that are the CSS selector of an element to be hidden on all sites, starting with a - /// class, e.g. `##.ad image`. - pub(crate) complex_class_rules: HashMap>, - /// Rules that are the CSS selector of an element to be hidden on all sites, starting with an - /// id, e.g. `###banner > .text a`. - pub(crate) complex_id_rules: HashMap>, - - pub(crate) specific_rules: HostnameRuleDb, - - /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit - /// into any of the class or id buckets above, e.g. `##a[href="https://malware.com"]` - pub(crate) misc_generic_selectors: HashSet, + filter_data_context: FilterDataContextRef, } -impl CosmeticFilterCache { - pub fn new() -> Self { - Self { - simple_class_rules: HashSet::new(), - simple_id_rules: HashSet::new(), - complex_class_rules: HashMap::new(), - complex_id_rules: HashMap::new(), - - specific_rules: HostnameRuleDb::default(), +/// Accumulates hostname-specific rules for a single domain before building HostnameSpecificRules +/// Note: hide and inject_script are now handled separately at the top level +#[derive(Default)] +struct HostnameRule { + unhide: Vec, + uninject_script: Vec, + procedural_action: Vec, + procedural_action_exception: Vec, +} - misc_generic_selectors: HashSet::new(), - } +impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for HostnameRule { + type Output = WIPOffset>; + + fn serialize( + value: Self, + builder: &mut B, + ) -> flatbuffers::WIPOffset> { + let unhide = serialize_vec_opt(value.unhide, builder); + let uninject_script = serialize_vec_opt(value.uninject_script, builder); + let procedural_action = serialize_vec_opt(value.procedural_action, builder); + let procedural_action_exception = + serialize_vec_opt(value.procedural_action_exception, builder); + + fb::HostnameSpecificRules::create( + builder.raw_builder(), + &fb::HostnameSpecificRulesArgs { + unhide, + uninject_script, + procedural_action, + procedural_action_exception, + }, + ) } +} - pub fn from_rules(rules: Vec) -> Self { - let mut self_ = Self { - simple_class_rules: HashSet::with_capacity(rules.len() / 2), - simple_id_rules: HashSet::with_capacity(rules.len() / 2), - complex_class_rules: HashMap::with_capacity(rules.len() / 2), - complex_id_rules: HashMap::with_capacity(rules.len() / 2), +#[derive(Default)] +pub(crate) struct CosmeticFilterCacheBuilder { + simple_class_rules: HashSet, + simple_id_rules: HashSet, + misc_generic_selectors: HashSet, + complex_class_rules: FlatMultiMapBuilder, + complex_id_rules: FlatMultiMapBuilder, - specific_rules: HostnameRuleDb::default(), + hostname_hide: FlatMultiMapBuilder, + hostname_inject_script: FlatMultiMapBuilder, - misc_generic_selectors: HashSet::with_capacity(rules.len() / 30), - }; + specific_rules: HashMap, +} + +impl CosmeticFilterCacheBuilder { + pub fn from_rules(rules: Vec) -> Self { + let mut self_ = Self::default(); for rule in rules { self_.add_filter(rule) @@ -119,7 +167,7 @@ impl CosmeticFilterCache { if let Some(generic_rule) = rule.hidden_generic_rule() { self.add_generic_filter(generic_rule); } - self.specific_rules.store_rule(rule); + self.store_hostname_rule(rule); } else { self.add_generic_filter(rule); } @@ -142,10 +190,8 @@ impl CosmeticFilterCache { let class = key[1..].to_string(); if key == selector { self.simple_class_rules.insert(class); - } else if let Some(bucket) = self.complex_class_rules.get_mut(&class) { - bucket.push(selector); } else { - self.complex_class_rules.insert(class, vec![selector]); + self.complex_class_rules.insert(class, selector); } } } else if selector.starts_with('#') { @@ -154,10 +200,8 @@ impl CosmeticFilterCache { let id = key[1..].to_string(); if key == selector { self.simple_id_rules.insert(id); - } else if let Some(bucket) = self.complex_id_rules.get_mut(&id) { - bucket.push(selector); } else { - self.complex_id_rules.insert(id, vec![selector]); + self.complex_id_rules.insert(id, selector); } } } else { @@ -165,6 +209,102 @@ impl CosmeticFilterCache { } } + // TODO: review this + fn store_hostname_rule(&mut self, rule: CosmeticFilter) { + use SpecificFilterType::*; + + let unhide = rule.mask.contains(CosmeticFilterMask::UNHIDE); + let script_inject = rule.mask.contains(CosmeticFilterMask::SCRIPT_INJECT); + + let kind = match ( + script_inject, + rule.plain_css_selector().map(|s| s.to_string()), + rule.action, + ) { + (false, Some(selector), None) => Hide(selector), + (true, Some(selector), None) => InjectScript((selector, rule.permission)), + (false, selector, action) => ProceduralOrAction( + serde_json::to_string(&ProceduralOrActionFilter { + selector: selector + .map(|selector| vec![CosmeticFilterOperator::CssSelector(selector)]) + .unwrap_or(rule.selector), + action, + }) + .unwrap(), + ), + (true, _, Some(_)) => return, // script injection with action - shouldn't be possible + (true, None, _) => return, // script injection without plain CSS selector - shouldn't be possible + }; + + let kind = if unhide { kind.negated() } else { kind }; + + let tokens_to_insert = std::iter::empty() + .chain(rule.hostnames.unwrap_or_default()) + .chain(rule.entities.unwrap_or_default()); + + tokens_to_insert.for_each(|t| self.store_hostname_filter(&t, kind.clone())); + + let tokens_to_insert_negated = std::iter::empty() + .chain(rule.not_hostnames.unwrap_or_default()) + .chain(rule.not_entities.unwrap_or_default()); + + let negated = kind.negated(); + + tokens_to_insert_negated.for_each(|t| self.store_hostname_filter(&t, negated.clone())); + } + + fn store_hostname_filter(&mut self, token: &Hash, kind: SpecificFilterType) { + use SpecificFilterType::*; + + match kind { + // Handle hide and inject_script at top level for better deduplication + Hide(s) => { + self.hostname_hide.insert(*token, s); + } + InjectScript((s, permission)) => { + let encoded_script = encode_script_with_permission(s, permission); + self.hostname_inject_script.insert(*token, encoded_script); + } + // Handle remaining types through HostnameRule + Unhide(s) => { + let entry = self.specific_rules.entry(*token).or_default(); + entry.unhide.push(s); + } + UninjectScript((s, _)) => { + let entry = self.specific_rules.entry(*token).or_default(); + entry.uninject_script.push(s); + } + ProceduralOrAction(s) => { + let entry = self.specific_rules.entry(*token).or_default(); + entry.procedural_action.push(s); + } + ProceduralOrActionException(s) => { + let entry = self.specific_rules.entry(*token).or_default(); + entry.procedural_action_exception.push(s); + } + } + } +} + +impl CosmeticFilterCache { + pub fn from_context(filter_data_context: FilterDataContextRef) -> Self { + Self { + filter_data_context, + } + } + + #[cfg(test)] + pub fn from_rules(rules: Vec) -> Self { + use crate::filters::{ + fb_builder::make_flatbuffer_from_rules, fb_network::FilterDataContext, + }; + + let memory = make_flatbuffer_from_rules(vec![], rules, true, 0); + + let filter_data_context = FilterDataContext::new(memory); + Self::from_context(filter_data_context) + } + /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they /// apply to all sites. Rather than injecting all of these rules onto every page, which would /// blow up memory usage, we only inject rules based on classes and ids that actually appear on @@ -191,34 +331,40 @@ impl CosmeticFilterCache { ) -> Vec { let mut selectors = vec![]; + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); + let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); + let complex_class_rules = FlatMapStringView::new( + cs.complex_class_rules_index(), + cs.complex_class_rules_values(), + ); + let complex_id_rules = + FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); + classes.into_iter().for_each(|class| { let class = class.as_ref(); - if self.simple_class_rules.contains(class) - && !exceptions.contains(&format!(".{}", class)) - { + if simple_class_rules.contains(class) && !exceptions.contains(&format!(".{}", class)) { selectors.push(format!(".{}", class)); } - if let Some(bucket) = self.complex_class_rules.get(class) { - selectors.extend( - bucket - .iter() - .filter(|sel| !exceptions.contains(*sel)) - .map(|s| s.to_owned()), - ); + if let Some(bucket) = complex_class_rules.get(class) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } } }); ids.into_iter().for_each(|id| { let id = id.as_ref(); - if self.simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { + if simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { selectors.push(format!("#{}", id)); } - if let Some(bucket) = self.complex_id_rules.get(id) { - selectors.extend( - bucket - .iter() - .filter(|sel| !exceptions.contains(*sel)) - .map(|s| s.to_owned()), - ); + if let Some(bucket) = complex_id_rules.get(id) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } } }); @@ -258,75 +404,77 @@ impl CosmeticFilterCache { .chain(request_hostnames.iter()) .collect(); - fn populate_set( - hash: &Hash, - source_bin: &HostnameFilterBin, - dest_set: &mut HashSet, - ) { - if let Some(s) = source_bin.get(hash) { - s.iter().for_each(|s| { - dest_set.insert(s.to_owned()); - }); - } - } + let cf = self.filter_data_context.memory.root().cosmetic_filters(); + let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); + let hostname_hide_view = + FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); + let hostname_inject_script_view = FlatMultiMapView::new( + cf.hostname_inject_script_index(), + cf.hostname_inject_script_values(), + ); + for hash in hashes.iter() { - populate_set( - hash, - &self.specific_rules.hide, - &mut specific_hide_selectors, - ); - populate_set( - hash, - &self.specific_rules.procedural_action, - &mut procedural_actions, - ); - // special behavior: `script_injections` doesn't have to own the strings yet, since the - // scripts need to be fetched and templated later - if let Some(s) = self.specific_rules.inject_script.get(hash) { - s.iter().for_each(|(s, mask)| { + // Handle top-level hide selectors + if let Some(hide_iterator) = hostname_hide_view.get(**hash) { + for (_, hide_selector) in hide_iterator { + if !exceptions.contains(hide_selector) { + specific_hide_selectors.insert(hide_selector.to_owned()); + } + } + } + + // Handle top-level inject scripts with encoded permissions + if let Some(script_iterator) = hostname_inject_script_view.get(**hash) { + for (_, encoded_script) in script_iterator { + let (permission, script) = decode_script_with_permission(encoded_script); script_injections - .entry(s) - .and_modify(|entry| *entry |= *mask) - .or_insert(*mask); - }); + .entry(script) + .and_modify(|entry| *entry |= permission) + .or_insert(permission); + } } - } - fn prune_set( - hash: &Hash, - source_bin: &HostnameFilterBin, - dest_set: &mut HashSet, - ) { - if let Some(s) = source_bin.get(hash) { - s.iter().for_each(|s| { - dest_set.remove(s); - }); + // Handle remaining rule types from HostnameSpecificRules + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process procedural actions + if let Some(procedural_actions_rules) = hostname_rules.procedural_action() { + for action in procedural_actions_rules.iter() { + procedural_actions.insert(action.to_owned()); + } + } } } + + // Process unhide/exception filters for hash in hashes.iter() { - // special behavior: unhide rules need to go in `exceptions` as well - if let Some(s) = self.specific_rules.unhide.get(hash) { - s.iter().for_each(|s| { - specific_hide_selectors.remove(s); - exceptions.insert(s.to_owned()); - }); - } - prune_set( - hash, - &self.specific_rules.procedural_action_exception, - &mut procedural_actions, - ); - // same logic but not using prune_set since strings are unowned, (see above) - if let Some(s) = self.specific_rules.uninject_script.get(hash) { - for s in s { - if s.is_empty() { - except_all_scripts = true; - script_injections.clear(); + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process unhide selectors (special behavior: they also go in exceptions) + if let Some(unhide_rules) = hostname_rules.unhide() { + for selector in unhide_rules.iter() { + specific_hide_selectors.remove(selector); + exceptions.insert(selector.to_owned()); } - if except_all_scripts { - continue; + } + + // Process procedural action exceptions + if let Some(procedural_exceptions) = hostname_rules.procedural_action_exception() { + for action in procedural_exceptions.iter() { + procedural_actions.remove(action); + } + } + + // Process script uninjects + if let Some(uninject_scripts) = hostname_rules.uninject_script() { + for script in uninject_scripts.iter() { + if script.is_empty() { + except_all_scripts = true; + script_injections.clear(); + } + if except_all_scripts { + continue; + } + script_injections.remove(script); } - script_injections.remove(s.as_str()); } } } @@ -334,11 +482,17 @@ impl CosmeticFilterCache { let hide_selectors = if generichide { specific_hide_selectors } else { - let mut hide_selectors = self - .misc_generic_selectors - .difference(&exceptions) - .cloned() - .collect::>(); + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let misc_generic_selectors_vector = cs.misc_generic_selectors(); + + // TODO: check performance of this + let mut hide_selectors = HashSet::new(); + for i in 0..misc_generic_selectors_vector.len() { + let selector = misc_generic_selectors_vector.get(i); + if !exceptions.contains(selector) { + hide_selectors.insert(selector.to_string()); + } + } specific_hide_selectors.into_iter().for_each(|sel| { hide_selectors.insert(sel); }); @@ -357,67 +511,46 @@ impl CosmeticFilterCache { } } -/// Each hostname-specific filter can be pointed to by several different hostnames, and each -/// hostname can correspond to several different filters. To effectively store and access those -/// filters by hostname, all the non-hostname information for filters is stored in per-hostname -/// "buckets" within a Vec, and each bucket is identified by its index. Hostname hashes are used as -/// keys to get the indices of relevant buckets, which are in turn used to retrieve all the filters -/// that apply. -#[derive(Default)] -pub(crate) struct HostnameFilterBin(pub HashMap>); - -impl HostnameFilterBin { - pub fn insert(&mut self, token: &Hash, filter: T) { - if let Some(bucket) = self.0.get_mut(token) { - bucket.push(filter); - } else { - self.0.insert(*token, vec![filter]); - } +impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for CosmeticFilterCacheBuilder { + type Output = WIPOffset>; + fn serialize(value: Self, builder: &mut B) -> WIPOffset> { + let complex_class_rules = FlatMultiMapBuilder::finish(value.complex_class_rules, builder); + let complex_id_rules = FlatMultiMapBuilder::finish(value.complex_id_rules, builder); + + // Handle top-level hostname hide and inject_script for better deduplication + let hostname_hide = FlatMultiMapBuilder::finish(value.hostname_hide, builder); + let hostname_inject_script = + FlatMultiMapBuilder::finish(value.hostname_inject_script, builder); + + // Handle remaining rule types through HostnameSpecificRules + let hostname_specific_rules = FlatMapBuilder::finish(value.specific_rules, builder); + + let simple_class_rules = Some(FlatSerialize::serialize(value.simple_class_rules, builder)); + let simple_id_rules = Some(FlatSerialize::serialize(value.simple_id_rules, builder)); + let misc_generic_selectors = Some(FlatSerialize::serialize( + value.misc_generic_selectors, + builder, + )); + + fb::CosmeticFilters::create( + builder.raw_builder(), + &fb::CosmeticFiltersArgs { + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index: Some(complex_class_rules.keys), + complex_class_rules_values: Some(complex_class_rules.values), + complex_id_rules_index: Some(complex_id_rules.keys), + complex_id_rules_values: Some(complex_id_rules.values), + hostname_hide_index: Some(hostname_hide.keys), + hostname_hide_values: Some(hostname_hide.values), + hostname_inject_script_index: Some(hostname_inject_script.keys), + hostname_inject_script_values: Some(hostname_inject_script.values), + hostname_index: Some(hostname_specific_rules.keys), + hostname_values: Some(hostname_specific_rules.values), + }, + ) } - - fn get(&self, token: &Hash) -> Option<&Vec> { - self.0.get(token) - } -} - -impl HostnameFilterBin { - /// Convenience method that serializes to JSON - pub fn insert_procedural_action_filter(&mut self, token: &Hash, f: &ProceduralOrActionFilter) { - self.insert(token, serde_json::to_string(f).unwrap()); - } -} - -/// Holds filter bins categorized by filter type. -#[derive(Default)] -pub(crate) struct HostnameRuleDb { - /// Simple hostname-specific hide rules, e.g. `example.com##.ad`. - /// - /// The parameter is the rule's CSS selector. - pub hide: HostnameFilterBin, - /// Simple hostname-specific hide exception rules, e.g. `example.com#@#.ad`. - /// - /// The parameter is the rule's CSS selector. - pub unhide: HostnameFilterBin, - /// Hostname-specific rules with a scriptlet to inject along with any arguments, e.g. - /// `example.com##+js(acis, Number.isNan)`. - /// - /// The parameter is the contents of the `+js(...)` syntax construct. - pub inject_script: HostnameFilterBin<(String, PermissionMask)>, - /// Hostname-specific rules to except a scriptlet to inject along with any arguments, e.g. - /// `example.com#@#+js(acis, Number.isNan)`. - /// - /// The parameter is the contents of the `+js(...)` syntax construct. - /// - /// In practice, these rules are extremely rare in filter lists. - pub uninject_script: HostnameFilterBin, - /// Procedural filters and/or filters with a [`CosmeticFilterAction`]. - /// - /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. - pub procedural_action: HostnameFilterBin, - /// Exceptions for procedural filters and/or filters with a [`CosmeticFilterAction`]. - /// - /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. - pub procedural_action_exception: HostnameFilterBin, } /// Representations of filters with complex behavior that relies on in-page JS logic. @@ -453,6 +586,7 @@ impl ProceduralOrActionFilter { } /// Convenience constructor for pure CSS style filters. + #[cfg(test)] pub(crate) fn from_css(selector: String, style: String) -> Self { Self { selector: vec![CosmeticFilterOperator::CssSelector(selector)], @@ -461,64 +595,6 @@ impl ProceduralOrActionFilter { } } -impl HostnameRuleDb { - pub fn store_rule(&mut self, rule: CosmeticFilter) { - use SpecificFilterType::*; - - let unhide = rule.mask.contains(CosmeticFilterMask::UNHIDE); - let script_inject = rule.mask.contains(CosmeticFilterMask::SCRIPT_INJECT); - - let kind = match ( - script_inject, - rule.plain_css_selector().map(|s| s.to_string()), - rule.action, - ) { - (false, Some(selector), None) => Hide(selector), - (true, Some(selector), None) => InjectScript((selector, rule.permission)), - (false, selector, action) => ProceduralOrAction( - serde_json::to_string(&ProceduralOrActionFilter { - selector: selector - .map(|selector| vec![CosmeticFilterOperator::CssSelector(selector)]) - .unwrap_or(rule.selector), - action, - }) - .unwrap(), - ), - (true, _, Some(_)) => return, // script injection with action - shouldn't be possible - (true, None, _) => return, // script injection without plain CSS selector - shouldn't be possible - }; - - let kind = if unhide { kind.negated() } else { kind }; - - let tokens_to_insert = std::iter::empty() - .chain(rule.hostnames.unwrap_or_default()) - .chain(rule.entities.unwrap_or_default()); - - tokens_to_insert.for_each(|t| self.store(&t, kind.clone())); - - let tokens_to_insert_negated = std::iter::empty() - .chain(rule.not_hostnames.unwrap_or_default()) - .chain(rule.not_entities.unwrap_or_default()); - - let negated = kind.negated(); - - tokens_to_insert_negated.for_each(|t| self.store(&t, negated.clone())); - } - - fn store(&mut self, token: &Hash, kind: SpecificFilterType) { - use SpecificFilterType::*; - - match kind { - Hide(s) => self.hide.insert(token, s), - Unhide(s) => self.unhide.insert(token, s), - InjectScript(s) => self.inject_script.insert(token, s), - UninjectScript((s, _)) => self.uninject_script.insert(token, s), - ProceduralOrAction(s) => self.procedural_action.insert(token, s), - ProceduralOrActionException(s) => self.procedural_action_exception.insert(token, s), - } - } -} - /// Exists to use common logic for binning filters correctly #[derive(Clone)] enum SpecificFilterType { diff --git a/src/data_format/mod.rs b/src/data_format/mod.rs deleted file mode 100644 index 0f8f5048..00000000 --- a/src/data_format/mod.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Allows serialization of the adblock engine into a compact binary format, as well as subsequent -//! rapid deserialization back into an engine. -//! -//! In order to support multiple format versions simultaneously, this module wraps around different -//! serialization/deserialization implementations and can automatically dispatch to the appropriate -//! one. - -mod storage; - -pub(crate) mod utils; - -use crate::cosmetic_filter_cache::CosmeticFilterCache; -use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; -use crate::network_filter_list::NetworkFilterListParsingError; - -/// Newer formats start with this magic byte sequence. -/// Calculated as the leading 4 bytes of `echo -n 'brave/adblock-rust' | sha512sum`. -const ADBLOCK_RUST_DAT_MAGIC: [u8; 4] = [0xd1, 0xd9, 0x3a, 0xaf]; -const ADBLOCK_RUST_DAT_VERSION: u8 = 1; - -#[derive(Debug)] -pub enum SerializationError { - RmpSerdeError(rmp_serde::encode::Error), -} - -impl From for SerializationError { - fn from(e: rmp_serde::encode::Error) -> Self { - Self::RmpSerdeError(e) - } -} - -#[derive(Debug)] -pub enum DeserializationError { - RmpSerdeError(rmp_serde::decode::Error), - UnsupportedFormatVersion(u8), - NoHeaderFound, - FlatBufferParsingError(flatbuffers::InvalidFlatbuffer), - ValidationError, -} - -impl From for DeserializationError { - fn from(x: std::convert::Infallible) -> Self { - match x {} - } -} - -impl From for DeserializationError { - fn from(e: rmp_serde::decode::Error) -> Self { - Self::RmpSerdeError(e) - } -} - -impl From for DeserializationError { - fn from(e: NetworkFilterListParsingError) -> Self { - match e { - NetworkFilterListParsingError::InvalidFlatbuffer(invalid_flatbuffer) => { - Self::FlatBufferParsingError(invalid_flatbuffer) - } - NetworkFilterListParsingError::UniqueDomainsOutOfBounds(_) => Self::ValidationError, - } - } -} - -pub(crate) fn serialize_engine( - flatbuffer_memory: &VerifiedFlatbufferMemory, - cfc: &CosmeticFilterCache, -) -> Result, SerializationError> { - let serialize_format = storage::SerializeFormat::from((flatbuffer_memory, cfc)); - serialize_format.serialize() -} - -pub(crate) fn deserialize_engine( - serialized: &[u8], -) -> Result<(VerifiedFlatbufferMemory, CosmeticFilterCache), DeserializationError> { - let deserialize_format = storage::DeserializeFormat::deserialize(serialized)?; - deserialize_format.try_into() -} - -// Verify the header (MAGIC + VERSION) and return the data after the header. -pub fn parse_dat_header(serialized: &[u8]) -> Result<&[u8], DeserializationError> { - if !serialized.starts_with(&ADBLOCK_RUST_DAT_MAGIC) { - return Err(DeserializationError::NoHeaderFound); - } - if serialized.len() < ADBLOCK_RUST_DAT_MAGIC.len() + 1 { - return Err(DeserializationError::NoHeaderFound); - } - let version = serialized[ADBLOCK_RUST_DAT_MAGIC.len()]; - if version != ADBLOCK_RUST_DAT_VERSION { - return Err(DeserializationError::UnsupportedFormatVersion(version)); - } - - Ok(&serialized[ADBLOCK_RUST_DAT_MAGIC.len() + 1..]) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn validate_magic_bytes() { - use sha2::Digest; - - let mut hasher = sha2::Sha512::new(); - - hasher.update("brave/adblock-rust"); - - let result = hasher.finalize(); - - assert!(result.starts_with(&ADBLOCK_RUST_DAT_MAGIC)); - } -} diff --git a/src/data_format/storage.rs b/src/data_format/storage.rs deleted file mode 100644 index 140d2bfc..00000000 --- a/src/data_format/storage.rs +++ /dev/null @@ -1,308 +0,0 @@ -//! Contains representations of data from the adblocking engine in a -//! forwards-and-backwards-compatible format, as well as utilities for converting these to and from -//! the actual `Engine` components. -//! -//! Any new fields should be added to the _end_ of both `SerializeFormat` and `DeserializeFormat`. - -use std::collections::{HashMap, HashSet}; - -use rmp_serde as rmps; -use serde::{Deserialize, Serialize}; - -use crate::cosmetic_filter_cache::{CosmeticFilterCache, HostnameRuleDb, ProceduralOrActionFilter}; -use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; -use crate::utils::Hash; - -use super::utils::{stabilize_hashmap_serialization, stabilize_hashset_serialization}; -use super::{DeserializationError, SerializationError}; - -/// Each variant describes a single rule that is specific to a particular hostname. -#[derive(Clone, Debug, Deserialize, Serialize)] -enum LegacySpecificFilterType { - Hide(String), - Unhide(String), - Style(String, String), - UnhideStyle(String, String), - ScriptInject(String), - UnhideScriptInject(String), -} - -#[derive(Deserialize, Serialize, Default)] -pub(crate) struct LegacyHostnameRuleDb { - #[serde(serialize_with = "stabilize_hashmap_serialization")] - db: HashMap>, -} - -impl From<&HostnameRuleDb> for LegacyHostnameRuleDb { - fn from(v: &HostnameRuleDb) -> Self { - let mut db = HashMap::>::new(); - for (hash, bin) in v.hide.0.iter() { - for f in bin { - db.entry(*hash) - .and_modify(|v| v.push(LegacySpecificFilterType::Hide(f.to_owned()))) - .or_insert_with(|| vec![LegacySpecificFilterType::Hide(f.to_owned())]); - } - } - for (hash, bin) in v.unhide.0.iter() { - for f in bin { - db.entry(*hash) - .and_modify(|v| v.push(LegacySpecificFilterType::Unhide(f.to_owned()))) - .or_insert_with(|| vec![LegacySpecificFilterType::Unhide(f.to_owned())]); - } - } - for (hash, bin) in v.inject_script.0.iter() { - for (f, _mask) in bin { - db.entry(*hash) - .and_modify(|v| v.push(LegacySpecificFilterType::ScriptInject(f.to_owned()))) - .or_insert_with(|| vec![LegacySpecificFilterType::ScriptInject(f.to_owned())]); - } - } - for (hash, bin) in v.uninject_script.0.iter() { - for f in bin { - db.entry(*hash) - .and_modify(|v| { - v.push(LegacySpecificFilterType::UnhideScriptInject(f.to_owned())) - }) - .or_insert_with(|| { - vec![LegacySpecificFilterType::UnhideScriptInject(f.to_owned())] - }); - } - } - for (hash, bin) in v.procedural_action.0.iter() { - for f in bin { - if let Ok(f) = serde_json::from_str::(f) { - if let Some((selector, style)) = f.as_css() { - db.entry(*hash) - .and_modify(|v| { - v.push(LegacySpecificFilterType::Style( - selector.clone(), - style.clone(), - )) - }) - .or_insert_with(|| { - vec![LegacySpecificFilterType::Style(selector, style)] - }); - } - } - } - } - for (hash, bin) in v.procedural_action_exception.0.iter() { - for f in bin { - if let Ok(f) = serde_json::from_str::(f) { - if let Some((selector, style)) = f.as_css() { - db.entry(*hash) - .and_modify(|v| { - v.push(LegacySpecificFilterType::UnhideStyle( - selector.to_owned(), - style.to_owned(), - )) - }) - .or_insert_with(|| { - vec![LegacySpecificFilterType::UnhideStyle( - selector.to_owned(), - style.to_owned(), - )] - }); - } - } - } - } - LegacyHostnameRuleDb { db } - } -} - -impl From for HostnameRuleDb { - fn from(val: LegacyHostnameRuleDb) -> Self { - use crate::cosmetic_filter_cache::HostnameFilterBin; - - let mut hide = HostnameFilterBin::default(); - let mut unhide = HostnameFilterBin::default(); - let mut procedural_action = HostnameFilterBin::default(); - let mut procedural_action_exception = HostnameFilterBin::default(); - let mut inject_script = HostnameFilterBin::default(); - let mut uninject_script = HostnameFilterBin::default(); - - for (hash, bin) in val.db.into_iter() { - for rule in bin.into_iter() { - match rule { - LegacySpecificFilterType::Hide(s) => hide.insert(&hash, s), - LegacySpecificFilterType::Unhide(s) => unhide.insert(&hash, s), - LegacySpecificFilterType::Style(s, st) => procedural_action - .insert_procedural_action_filter( - &hash, - &ProceduralOrActionFilter::from_css(s, st), - ), - LegacySpecificFilterType::UnhideStyle(s, st) => procedural_action_exception - .insert_procedural_action_filter( - &hash, - &ProceduralOrActionFilter::from_css(s, st), - ), - LegacySpecificFilterType::ScriptInject(s) => { - inject_script.insert(&hash, (s, Default::default())) - } - LegacySpecificFilterType::UnhideScriptInject(s) => { - uninject_script.insert(&hash, s) - } - } - } - } - HostnameRuleDb { - hide, - unhide, - inject_script, - uninject_script, - procedural_action, - procedural_action_exception, - } - } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -pub(crate) struct LegacyRedirectResource { - pub content_type: String, - pub data: String, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] -pub(crate) struct LegacyRedirectResourceStorage { - #[serde(serialize_with = "stabilize_hashmap_serialization")] - pub resources: HashMap, -} - -#[derive(Clone, Deserialize, Serialize)] -pub(crate) struct LegacyScriptletResource { - scriptlet: String, -} - -#[derive(Default, Deserialize, Serialize)] -pub(crate) struct LegacyScriptletResourceStorage { - #[serde(serialize_with = "stabilize_hashmap_serialization")] - resources: HashMap, -} - -/// Provides structural aggregration of referenced adblock engine data to allow for allocation-free -/// serialization. -#[derive(Serialize)] -pub(crate) struct SerializeFormat<'a> { - flatbuffer_memory: Vec, - - resources: LegacyRedirectResourceStorage, - - #[serde(serialize_with = "stabilize_hashset_serialization")] - simple_class_rules: &'a HashSet, - #[serde(serialize_with = "stabilize_hashset_serialization")] - simple_id_rules: &'a HashSet, - #[serde(serialize_with = "stabilize_hashmap_serialization")] - complex_class_rules: &'a HashMap>, - #[serde(serialize_with = "stabilize_hashmap_serialization")] - complex_id_rules: &'a HashMap>, - - specific_rules: LegacyHostnameRuleDb, - - #[serde(serialize_with = "stabilize_hashset_serialization")] - misc_generic_selectors: &'a HashSet, - - scriptlets: LegacyScriptletResourceStorage, - - #[serde(serialize_with = "stabilize_hashmap_serialization")] - procedural_action: &'a HashMap>, - #[serde(serialize_with = "stabilize_hashmap_serialization")] - procedural_action_exception: &'a HashMap>, -} - -impl SerializeFormat<'_> { - pub fn serialize(&self) -> Result, SerializationError> { - let mut output = super::ADBLOCK_RUST_DAT_MAGIC.to_vec(); - output.push(super::ADBLOCK_RUST_DAT_VERSION); - rmps::encode::write(&mut output, &self)?; - Ok(output) - } -} - -/// Structural representation of adblock engine data that can be built up from deserialization and -/// used directly to construct new `Engine` components without unnecessary allocation. -#[derive(Deserialize)] -pub(crate) struct DeserializeFormat { - flatbuffer_memory: Vec, - - _resources: LegacyRedirectResourceStorage, - - simple_class_rules: HashSet, - simple_id_rules: HashSet, - complex_class_rules: HashMap>, - complex_id_rules: HashMap>, - - specific_rules: LegacyHostnameRuleDb, - - misc_generic_selectors: HashSet, - - _scriptlets: LegacyScriptletResourceStorage, - - #[serde(default)] - procedural_action: HashMap>, - #[serde(default)] - procedural_action_exception: HashMap>, -} - -impl DeserializeFormat { - pub fn deserialize(serialized: &[u8]) -> Result { - let data = super::parse_dat_header(serialized)?; - let format: Self = rmps::decode::from_read(data)?; - Ok(format) - } -} - -impl<'a> From<(&'a VerifiedFlatbufferMemory, &'a CosmeticFilterCache)> for SerializeFormat<'a> { - fn from(v: (&'a VerifiedFlatbufferMemory, &'a CosmeticFilterCache)) -> Self { - let (memory, cfc) = v; - Self { - flatbuffer_memory: memory.data().to_vec(), - - resources: LegacyRedirectResourceStorage::default(), - - simple_class_rules: &cfc.simple_class_rules, - simple_id_rules: &cfc.simple_id_rules, - complex_class_rules: &cfc.complex_class_rules, - complex_id_rules: &cfc.complex_id_rules, - - specific_rules: (&cfc.specific_rules).into(), - - misc_generic_selectors: &cfc.misc_generic_selectors, - - scriptlets: LegacyScriptletResourceStorage::default(), - - procedural_action: &cfc.specific_rules.procedural_action.0, - procedural_action_exception: &cfc.specific_rules.procedural_action_exception.0, - } - } -} - -impl TryFrom for (VerifiedFlatbufferMemory, CosmeticFilterCache) { - fn try_from(v: DeserializeFormat) -> Result { - use crate::cosmetic_filter_cache::HostnameFilterBin; - - let mut specific_rules: HostnameRuleDb = v.specific_rules.into(); - specific_rules.procedural_action = HostnameFilterBin(v.procedural_action); - specific_rules.procedural_action_exception = - HostnameFilterBin(v.procedural_action_exception); - - let memory = VerifiedFlatbufferMemory::from_raw(v.flatbuffer_memory) - .map_err(DeserializationError::FlatBufferParsingError)?; - - Ok(( - memory, - CosmeticFilterCache { - simple_class_rules: v.simple_class_rules, - simple_id_rules: v.simple_id_rules, - complex_class_rules: v.complex_class_rules, - complex_id_rules: v.complex_id_rules, - - specific_rules, - - misc_generic_selectors: v.misc_generic_selectors, - }, - )) - } - - type Error = DeserializationError; -} diff --git a/src/data_format/utils.rs b/src/data_format/utils.rs deleted file mode 100644 index 3b3b3e81..00000000 --- a/src/data_format/utils.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Common utilities associated with serialization and deserialization of the `Engine` data into -//! binary formats. - -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; - -use serde::{Serialize, Serializer}; - -/// Forces a `HashSet` to be serialized with a stable ordering by temporarily representing it as a -/// `BTreeSet`. -pub fn stabilize_hashset_serialization(set: &HashSet, s: S) -> Result -where - S: Serializer, - V: Ord + serde::Serialize, -{ - let stabilized: BTreeSet<&V> = set.iter().collect(); - stabilized.serialize(s) -} - -/// Forces a `HashMap` to be serialized with a stable ordering by temporarily representing it as a -/// `BTreeMap`. -pub fn stabilize_hashmap_serialization( - set: &HashMap, - s: S, -) -> Result -where - S: Serializer, - K: Ord + Serialize, - V: Serialize, -{ - let stabilized: BTreeMap<&K, &V> = set.iter().collect(); - stabilized.serialize(s) -} diff --git a/src/engine.rs b/src/engine.rs index ce1e6cd5..b2020bbe 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,8 +2,9 @@ use crate::blocker::{Blocker, BlockerResult}; use crate::cosmetic_filter_cache::{CosmeticFilterCache, UrlSpecificResources}; -use crate::filters::fb_builder::make_flatbuffer; +use crate::filters::fb_builder::make_flatbuffer_from_rules; use crate::filters::fb_network::{FilterDataContext, FilterDataContextRef}; +use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; use crate::lists::{FilterSet, ParseOptions}; use crate::regex_manager::RegexManagerDiscardPolicy; use crate::request::Request; @@ -51,13 +52,24 @@ pub struct Engine { filter_data_context: FilterDataContextRef, } +const ADBLOCK_FLATBUFFER_VERSION: u32 = 1; + +#[derive(Debug)] +pub enum DeserializationError { + VersionMismatch(u32), + FlatBufferParsingError(flatbuffers::InvalidFlatbuffer), + ValidationError, +} + impl Default for Engine { fn default() -> Self { let filter_data_context = FilterDataContextRef::new(Default::default()); Self { blocker: Blocker::from_context(FilterDataContextRef::clone(&filter_data_context)), - cosmetic_cache: CosmeticFilterCache::new(), + cosmetic_cache: CosmeticFilterCache::from_context(FilterDataContextRef::clone( + &filter_data_context, + )), resources: ResourceStorage::default(), filter_data_context, } @@ -103,13 +115,20 @@ impl Engine { .. } = set; - let memory = make_flatbuffer(network_filters, optimize); + let memory = make_flatbuffer_from_rules( + network_filters, + cosmetic_filters, + optimize, + ADBLOCK_FLATBUFFER_VERSION, + ); let filter_data_context = FilterDataContext::new(memory); Self { blocker: Blocker::from_context(FilterDataContextRef::clone(&filter_data_context)), - cosmetic_cache: CosmeticFilterCache::from_rules(cosmetic_filters), + cosmetic_cache: CosmeticFilterCache::from_context(FilterDataContextRef::clone( + &filter_data_context, + )), resources: ResourceStorage::default(), filter_data_context, } @@ -240,8 +259,8 @@ impl Engine { } /// Serializes the `Engine` into a binary format so that it can be quickly reloaded later. - pub fn serialize(&self) -> Result, crate::data_format::SerializationError> { - crate::data_format::serialize_engine(&self.filter_data_context.memory, &self.cosmetic_cache) + pub fn serialize(&self) -> &[u8] { + self.filter_data_context.memory.data() } /// Deserialize the `Engine` from the binary format generated by `Engine::serialize`. @@ -249,18 +268,24 @@ impl Engine { /// Note that the binary format has a built-in version number that may be incremented. There is /// no guarantee that later versions of the format will be deserializable across minor versions /// of adblock-rust; the format is provided only as a caching optimization. - pub fn deserialize( - &mut self, - serialized: &[u8], - ) -> Result<(), crate::data_format::DeserializationError> { + pub fn deserialize(&mut self, serialized: &[u8]) -> Result<(), DeserializationError> { let current_tags = self.blocker.tags_enabled(); - let (memory, cosmetic_cache) = crate::data_format::deserialize_engine(serialized)?; - self.filter_data_context = FilterDataContext::new(memory); + let memory = VerifiedFlatbufferMemory::from_raw(serialized.to_vec()) + .map_err(DeserializationError::FlatBufferParsingError)?; + if memory.root().version() != ADBLOCK_FLATBUFFER_VERSION { + return Err(DeserializationError::VersionMismatch( + memory.root().version(), + )); + } + let context = FilterDataContext::new(memory); + self.filter_data_context = context; self.blocker = Blocker::from_context(FilterDataContextRef::clone(&self.filter_data_context)); self.blocker .use_tags(¤t_tags.iter().map(|s| &**s).collect::>()); - self.cosmetic_cache = cosmetic_cache; + self.cosmetic_cache = CosmeticFilterCache::from_context(FilterDataContextRef::clone( + &self.filter_data_context, + )); Ok(()) } } diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 9775fa6b..5b83ccae 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -8,6 +8,8 @@ use std::vec; use flatbuffers::WIPOffset; +use crate::cosmetic_filter_cache::CosmeticFilterCacheBuilder; +use crate::filters::cosmetic::CosmeticFilter; use crate::filters::network::{NetworkFilter, NetworkFilterMaskHelper}; use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, FlatSerialize, WIPFlatVec}; @@ -57,15 +59,18 @@ impl<'a> EngineFlatBuilder<'a> { pub fn finish( &mut self, network_rules: WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>, + cosmetic_rules: WIPOffset>, + version: u32, ) -> VerifiedFlatbufferMemory { let unique_domains_hashes = Some(self.fb_builder.create_vector(&self.unique_domains_hashes)); - let network_rules = Some(network_rules); let engine = fb::Engine::create( self.raw_builder(), &fb::EngineArgs { - network_rules, + version, + network_rules: Some(network_rules), unique_domains_hashes, + cosmetic_filters: Some(cosmetic_rules), }, ); self.raw_builder().finish(engine, None); @@ -339,12 +344,16 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder { } } -pub fn make_flatbuffer( +pub fn make_flatbuffer_from_rules( network_filters: Vec, + cosmetic_filters: Vec, optimize: bool, + version: u32, ) -> VerifiedFlatbufferMemory { let mut builder = EngineFlatBuilder::default(); let network_rules_builder = NetworkRulesBuilder::from_rules(network_filters, optimize); let network_rules = FlatSerialize::serialize(network_rules_builder, &mut builder); - builder.finish(network_rules) + let cosmetic_rules = CosmeticFilterCacheBuilder::from_rules(cosmetic_filters); + let cosmetic_rules = FlatSerialize::serialize(cosmetic_rules, &mut builder); + builder.finish(network_rules, cosmetic_rules, version) } diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index 7e680502..93abb1a3 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; -use crate::filters::fb_builder::make_flatbuffer; use crate::filters::network::{NetworkFilterMask, NetworkFilterMaskHelper, NetworkMatchable}; use crate::flatbuffers::unsafe_tools::{fb_vector_to_slice, VerifiedFlatbufferMemory}; @@ -91,7 +90,12 @@ pub(crate) struct FilterDataContext { impl Default for FilterDataContext { fn default() -> Self { Self { - memory: make_flatbuffer(vec![], false), + memory: crate::filters::fb_builder::make_flatbuffer_from_rules( + vec![], + vec![], + false, + 0, + ), unique_domains_hashes_map: HashMap::new(), } } diff --git a/src/flatbuffers/containers/flat_map.rs b/src/flatbuffers/containers/flat_map.rs new file mode 100644 index 00000000..509eeb2e --- /dev/null +++ b/src/flatbuffers/containers/flat_map.rs @@ -0,0 +1,69 @@ +use std::marker::PhantomData; + +use crate::flatbuffers::containers; +use containers::flat_serialize::{FlatBuilder, FlatMapBuilderOutput, FlatSerialize}; +use containers::sorted_index::SortedIndex; +use flatbuffers::{Follow, Vector}; + +pub(crate) struct FlatMapView<'a, I: Ord, V, Keys> +where + Keys: SortedIndex, + V: Follow<'a>, +{ + keys: Keys, + values: Vector<'a, V>, + _phantom: PhantomData, +} + +impl<'a, I: Ord + Copy, V, Keys> FlatMapView<'a, I, V, Keys> +where + Keys: SortedIndex + Clone, + V: flatbuffers::Follow<'a>, +{ + pub fn new(keys: Keys, values: Vector<'a, V>) -> Self { + debug_assert!(keys.len() == values.len()); + Self { + keys, + values, + _phantom: PhantomData, + } + } + + pub fn get(&self, key: I) -> Option<>::Inner> { + let index = self.keys.partition_point(|x| *x < key); + if index < self.keys.len() && self.keys.get(index) == key { + Some(self.values.get(index)) + } else { + None + } + } +} + +pub(crate) struct FlatMapBuilder; + +impl FlatMapBuilder { + pub fn finish<'a, I, V, B: FlatBuilder<'a>>( + value: std::collections::HashMap, + builder: &mut B, + ) -> FlatMapBuilderOutput<'a, I, V, B> + where + I: FlatSerialize<'a, B> + Ord, + V: FlatSerialize<'a, B>, + { + let mut entries: Vec<_> = value.into_iter().collect(); + entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); + + let mut indexes = Vec::with_capacity(entries.len()); + let mut values = Vec::with_capacity(entries.len()); + + for (key, value) in entries.into_iter() { + indexes.push(FlatSerialize::serialize(key, builder)); + values.push(FlatSerialize::serialize(value, builder)); + } + + FlatMapBuilderOutput { + keys: builder.raw_builder().create_vector(&indexes), + values: builder.raw_builder().create_vector(&values), + } + } +} diff --git a/src/flatbuffers/containers/flat_multimap.rs b/src/flatbuffers/containers/flat_multimap.rs index 99b6255f..6ccb28a7 100644 --- a/src/flatbuffers/containers/flat_multimap.rs +++ b/src/flatbuffers/containers/flat_multimap.rs @@ -129,6 +129,9 @@ impl FlatMultiMapBuilder { } } +pub(crate) type FlatMapStringView<'a, V> = + FlatMultiMapView<'a, &'a str, V, Vector<'a, flatbuffers::ForwardsUOffset<&'a str>>>; + #[cfg(test)] #[path = "../../../tests/unit/flatbuffers/containers/flat_multimap.rs"] mod unit_tests; diff --git a/src/flatbuffers/containers/mod.rs b/src/flatbuffers/containers/mod.rs index 50164fd2..20eb251d 100644 --- a/src/flatbuffers/containers/mod.rs +++ b/src/flatbuffers/containers/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod flat_map; pub(crate) mod flat_multimap; pub(crate) mod flat_serialize; pub(crate) mod flat_set; diff --git a/src/flatbuffers/fb_network_filter.fbs b/src/flatbuffers/fb_network_filter.fbs index 332a91ae..b281b31c 100644 --- a/src/flatbuffers/fb_network_filter.fbs +++ b/src/flatbuffers/fb_network_filter.fbs @@ -29,10 +29,56 @@ table NetworkFilterList { filter_map_values: [NetworkFilter] (required); } +table HostnameSpecificRules { + unhide: [string]; + uninject_script: [string]; + procedural_action: [string]; + procedural_action_exception: [string]; +} + +table CosmeticFilters { + /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. + simple_class_rules: [string] (required); + + /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. + simple_id_rules: [string] (required); + + /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit + /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` + misc_generic_selectors: [string] (required); + + /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` + /// These are stored as a multi-map from class name to list of selectors + complex_class_rules_index: [string] (required); + complex_class_rules_values: [string] (required); + + /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` + /// These are stored as a multi-map from id name to list of selectors + complex_id_rules_index: [string] (required); + complex_id_rules_values: [string] (required); + + /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors + hostname_hide_index: [uint64] (required); + hostname_hide_values: [string] (required); + + /// Hostname-specific script injection filters - multi-map from hostname hash to script data + /// First byte of each script encodes permission bits to avoid separate permissions array + hostname_inject_script_index: [uint64] (required); + hostname_inject_script_values: [string] (required); + + // Map from hostname(hash) => HostnameSpecificRules using FlatMultiMapBuilder + // Store only one item per domain using FlatMultiMapBuilder (for remaining rule types) + hostname_index: [uint64] (required); + hostname_values: [HostnameSpecificRules] (required); +} + // A root type containing a serialized Engine. // Currently it contains only some of engine fields: // network filters and supporing struct. table Engine { + // Format version. Should be increased when makeing non back-compatible changes. + version: uint32; + // Contains several NetworkFilterList matching to different kinds of lists. // The indexes are matching NetworkFilterListId. // The size must be NetworkFilterListId::Size. @@ -40,6 +86,8 @@ table Engine { // Contains hashes for opt_(not)_domains. See opt_domains for details. unique_domains_hashes: [uint64] (required); + + cosmetic_filters: CosmeticFilters (required); } root_type Engine; diff --git a/src/flatbuffers/fb_network_filter_generated.rs b/src/flatbuffers/fb_network_filter_generated.rs index 5b1e7ece..a36d9e8a 100644 --- a/src/flatbuffers/fb_network_filter_generated.rs +++ b/src/flatbuffers/fb_network_filter_generated.rs @@ -1,9 +1,10 @@ // automatically generated by the FlatBuffers compiler, do not modify + // @generated -use core::cmp::Ordering; use core::mem; +use core::cmp::Ordering; extern crate flatbuffers; use self::flatbuffers::{EndianScalar, Follow}; @@ -11,946 +12,1491 @@ use self::flatbuffers::{EndianScalar, Follow}; #[allow(unused_imports, dead_code)] pub mod fb { - use core::cmp::Ordering; - use core::mem; + use core::mem; + use core::cmp::Ordering; - extern crate flatbuffers; - use self::flatbuffers::{EndianScalar, Follow}; + extern crate flatbuffers; + use self::flatbuffers::{EndianScalar, Follow}; - pub enum NetworkFilterOffset {} - #[derive(Copy, Clone, PartialEq)] +pub enum NetworkFilterOffset {} +#[derive(Copy, Clone, PartialEq)] - pub struct NetworkFilter<'a> { - pub _tab: flatbuffers::Table<'a>, - } +pub struct NetworkFilter<'a> { + pub _tab: flatbuffers::Table<'a>, +} - impl<'a> flatbuffers::Follow<'a> for NetworkFilter<'a> { - type Inner = NetworkFilter<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { - _tab: flatbuffers::Table::new(buf, loc), - } - } - } +impl<'a> flatbuffers::Follow<'a> for NetworkFilter<'a> { + type Inner = NetworkFilter<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: flatbuffers::Table::new(buf, loc) } + } +} - impl<'a> NetworkFilter<'a> { - pub const VT_MASK: flatbuffers::VOffsetT = 4; - pub const VT_OPT_DOMAINS: flatbuffers::VOffsetT = 6; - pub const VT_OPT_NOT_DOMAINS: flatbuffers::VOffsetT = 8; - pub const VT_PATTERNS: flatbuffers::VOffsetT = 10; - pub const VT_MODIFIER_OPTION: flatbuffers::VOffsetT = 12; - pub const VT_HOSTNAME: flatbuffers::VOffsetT = 14; - pub const VT_TAG: flatbuffers::VOffsetT = 16; - pub const VT_RAW_LINE: flatbuffers::VOffsetT = 18; - - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - NetworkFilter { _tab: table } - } - #[allow(unused_mut)] - pub fn create< - 'bldr: 'args, - 'args: 'mut_bldr, - 'mut_bldr, - A: flatbuffers::Allocator + 'bldr, - >( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args NetworkFilterArgs<'args>, - ) -> flatbuffers::WIPOffset> { - let mut builder = NetworkFilterBuilder::new(_fbb); - if let Some(x) = args.raw_line { - builder.add_raw_line(x); - } - if let Some(x) = args.tag { - builder.add_tag(x); - } - if let Some(x) = args.hostname { - builder.add_hostname(x); - } - if let Some(x) = args.modifier_option { - builder.add_modifier_option(x); - } - if let Some(x) = args.patterns { - builder.add_patterns(x); - } - if let Some(x) = args.opt_not_domains { - builder.add_opt_not_domains(x); - } - if let Some(x) = args.opt_domains { - builder.add_opt_domains(x); - } - builder.add_mask(args.mask); - builder.finish() - } - - pub fn unpack(&self) -> NetworkFilterT { - let mask = self.mask(); - let opt_domains = self.opt_domains().map(|x| x.into_iter().collect()); - let opt_not_domains = self.opt_not_domains().map(|x| x.into_iter().collect()); - let patterns = self - .patterns() - .map(|x| x.iter().map(|s| s.to_string()).collect()); - let modifier_option = self.modifier_option().map(|x| x.to_string()); - let hostname = self.hostname().map(|x| x.to_string()); - let tag = self.tag().map(|x| x.to_string()); - let raw_line = self.raw_line().map(|x| x.to_string()); - NetworkFilterT { - mask, - opt_domains, - opt_not_domains, - patterns, - modifier_option, - hostname, - tag, - raw_line, - } - } - - #[inline] - pub fn mask(&self) -> u32 { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::(NetworkFilter::VT_MASK, Some(0)) - .unwrap() - } - } - #[inline] - pub fn opt_domains(&self) -> Option> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>>( - NetworkFilter::VT_OPT_DOMAINS, - None, - ) - } - } - #[inline] - pub fn opt_not_domains(&self) -> Option> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>>( - NetworkFilter::VT_OPT_NOT_DOMAINS, - None, - ) - } - } - #[inline] - pub fn patterns( - &self, - ) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab.get::>, - >>(NetworkFilter::VT_PATTERNS, None) - } - } - #[inline] - pub fn modifier_option(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab.get::>( - NetworkFilter::VT_MODIFIER_OPTION, - None, - ) - } - } - #[inline] - pub fn hostname(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>(NetworkFilter::VT_HOSTNAME, None) - } - } - #[inline] - pub fn tag(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>(NetworkFilter::VT_TAG, None) - } - } - #[inline] - pub fn raw_line(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>(NetworkFilter::VT_RAW_LINE, None) - } - } - } +impl<'a> NetworkFilter<'a> { + pub const VT_MASK: flatbuffers::VOffsetT = 4; + pub const VT_OPT_DOMAINS: flatbuffers::VOffsetT = 6; + pub const VT_OPT_NOT_DOMAINS: flatbuffers::VOffsetT = 8; + pub const VT_PATTERNS: flatbuffers::VOffsetT = 10; + pub const VT_MODIFIER_OPTION: flatbuffers::VOffsetT = 12; + pub const VT_HOSTNAME: flatbuffers::VOffsetT = 14; + pub const VT_TAG: flatbuffers::VOffsetT = 16; + pub const VT_RAW_LINE: flatbuffers::VOffsetT = 18; - impl flatbuffers::Verifiable for NetworkFilter<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, - pos: usize, - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("mask", Self::VT_MASK, false)? - .visit_field::>>( - "opt_domains", - Self::VT_OPT_DOMAINS, - false, - )? - .visit_field::>>( - "opt_not_domains", - Self::VT_OPT_NOT_DOMAINS, - false, - )? - .visit_field::>, - >>("patterns", Self::VT_PATTERNS, false)? - .visit_field::>( - "modifier_option", - Self::VT_MODIFIER_OPTION, - false, - )? - .visit_field::>( - "hostname", - Self::VT_HOSTNAME, - false, - )? - .visit_field::>("tag", Self::VT_TAG, false)? - .visit_field::>( - "raw_line", - Self::VT_RAW_LINE, - false, - )? - .finish(); - Ok(()) - } - } - pub struct NetworkFilterArgs<'a> { - pub mask: u32, - pub opt_domains: Option>>, - pub opt_not_domains: Option>>, - pub patterns: Option< - flatbuffers::WIPOffset>>, - >, - pub modifier_option: Option>, - pub hostname: Option>, - pub tag: Option>, - pub raw_line: Option>, - } - impl<'a> Default for NetworkFilterArgs<'a> { - #[inline] - fn default() -> Self { - NetworkFilterArgs { - mask: 0, - opt_domains: None, - opt_not_domains: None, - patterns: None, - modifier_option: None, - hostname: None, - tag: None, - raw_line: None, - } - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + NetworkFilter { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NetworkFilterArgs<'args> + ) -> flatbuffers::WIPOffset> { + let mut builder = NetworkFilterBuilder::new(_fbb); + if let Some(x) = args.raw_line { builder.add_raw_line(x); } + if let Some(x) = args.tag { builder.add_tag(x); } + if let Some(x) = args.hostname { builder.add_hostname(x); } + if let Some(x) = args.modifier_option { builder.add_modifier_option(x); } + if let Some(x) = args.patterns { builder.add_patterns(x); } + if let Some(x) = args.opt_not_domains { builder.add_opt_not_domains(x); } + if let Some(x) = args.opt_domains { builder.add_opt_domains(x); } + builder.add_mask(args.mask); + builder.finish() + } - pub struct NetworkFilterBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, - } - impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterBuilder<'a, 'b, A> { - #[inline] - pub fn add_mask(&mut self, mask: u32) { - self.fbb_.push_slot::(NetworkFilter::VT_MASK, mask, 0); - } - #[inline] - pub fn add_opt_domains( - &mut self, - opt_domains: flatbuffers::WIPOffset>, - ) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_OPT_DOMAINS, - opt_domains, - ); - } - #[inline] - pub fn add_opt_not_domains( - &mut self, - opt_not_domains: flatbuffers::WIPOffset>, - ) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_OPT_NOT_DOMAINS, - opt_not_domains, - ); - } - #[inline] - pub fn add_patterns( - &mut self, - patterns: flatbuffers::WIPOffset< - flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, - >, - ) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_PATTERNS, - patterns, - ); - } - #[inline] - pub fn add_modifier_option(&mut self, modifier_option: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_MODIFIER_OPTION, - modifier_option, - ); - } - #[inline] - pub fn add_hostname(&mut self, hostname: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_HOSTNAME, - hostname, - ); - } - #[inline] - pub fn add_tag(&mut self, tag: flatbuffers::WIPOffset<&'b str>) { - self.fbb_ - .push_slot_always::>(NetworkFilter::VT_TAG, tag); - } - #[inline] - pub fn add_raw_line(&mut self, raw_line: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>( - NetworkFilter::VT_RAW_LINE, - raw_line, - ); - } - #[inline] - pub fn new( - _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - ) -> NetworkFilterBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - NetworkFilterBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + pub fn unpack(&self) -> NetworkFilterT { + let mask = self.mask(); + let opt_domains = self.opt_domains().map(|x| { + x.into_iter().collect() + }); + let opt_not_domains = self.opt_not_domains().map(|x| { + x.into_iter().collect() + }); + let patterns = self.patterns().map(|x| { + x.iter().map(|s| s.to_string()).collect() + }); + let modifier_option = self.modifier_option().map(|x| { + x.to_string() + }); + let hostname = self.hostname().map(|x| { + x.to_string() + }); + let tag = self.tag().map(|x| { + x.to_string() + }); + let raw_line = self.raw_line().map(|x| { + x.to_string() + }); + NetworkFilterT { + mask, + opt_domains, + opt_not_domains, + patterns, + modifier_option, + hostname, + tag, + raw_line, } + } - impl core::fmt::Debug for NetworkFilter<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("NetworkFilter"); - ds.field("mask", &self.mask()); - ds.field("opt_domains", &self.opt_domains()); - ds.field("opt_not_domains", &self.opt_not_domains()); - ds.field("patterns", &self.patterns()); - ds.field("modifier_option", &self.modifier_option()); - ds.field("hostname", &self.hostname()); - ds.field("tag", &self.tag()); - ds.field("raw_line", &self.raw_line()); - ds.finish() - } - } - #[non_exhaustive] - #[derive(Debug, Clone, PartialEq)] - pub struct NetworkFilterT { - pub mask: u32, - pub opt_domains: Option>, - pub opt_not_domains: Option>, - pub patterns: Option>, - pub modifier_option: Option, - pub hostname: Option, - pub tag: Option, - pub raw_line: Option, - } - impl Default for NetworkFilterT { - fn default() -> Self { - Self { - mask: 0, - opt_domains: None, - opt_not_domains: None, - patterns: None, - modifier_option: None, - hostname: None, - tag: None, - raw_line: None, - } - } - } - impl NetworkFilterT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, - ) -> flatbuffers::WIPOffset> { - let mask = self.mask; - let opt_domains = self.opt_domains.as_ref().map(|x| _fbb.create_vector(x)); - let opt_not_domains = self.opt_not_domains.as_ref().map(|x| _fbb.create_vector(x)); - let patterns = self.patterns.as_ref().map(|x| { - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); - _fbb.create_vector(&w) - }); - let modifier_option = self.modifier_option.as_ref().map(|x| _fbb.create_string(x)); - let hostname = self.hostname.as_ref().map(|x| _fbb.create_string(x)); - let tag = self.tag.as_ref().map(|x| _fbb.create_string(x)); - let raw_line = self.raw_line.as_ref().map(|x| _fbb.create_string(x)); - NetworkFilter::create( - _fbb, - &NetworkFilterArgs { - mask, - opt_domains, - opt_not_domains, - patterns, - modifier_option, - hostname, - tag, - raw_line, - }, - ) - } - } - pub enum NetworkFilterListOffset {} - #[derive(Copy, Clone, PartialEq)] + #[inline] + pub fn mask(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(NetworkFilter::VT_MASK, Some(0)).unwrap()} + } + #[inline] + pub fn opt_domains(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(NetworkFilter::VT_OPT_DOMAINS, None)} + } + #[inline] + pub fn opt_not_domains(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(NetworkFilter::VT_OPT_NOT_DOMAINS, None)} + } + #[inline] + pub fn patterns(&self) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(NetworkFilter::VT_PATTERNS, None)} + } + #[inline] + pub fn modifier_option(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>(NetworkFilter::VT_MODIFIER_OPTION, None)} + } + #[inline] + pub fn hostname(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>(NetworkFilter::VT_HOSTNAME, None)} + } + #[inline] + pub fn tag(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>(NetworkFilter::VT_TAG, None)} + } + #[inline] + pub fn raw_line(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>(NetworkFilter::VT_RAW_LINE, None)} + } +} - pub struct NetworkFilterList<'a> { - pub _tab: flatbuffers::Table<'a>, +impl flatbuffers::Verifiable for NetworkFilter<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("mask", Self::VT_MASK, false)? + .visit_field::>>("opt_domains", Self::VT_OPT_DOMAINS, false)? + .visit_field::>>("opt_not_domains", Self::VT_OPT_NOT_DOMAINS, false)? + .visit_field::>>>("patterns", Self::VT_PATTERNS, false)? + .visit_field::>("modifier_option", Self::VT_MODIFIER_OPTION, false)? + .visit_field::>("hostname", Self::VT_HOSTNAME, false)? + .visit_field::>("tag", Self::VT_TAG, false)? + .visit_field::>("raw_line", Self::VT_RAW_LINE, false)? + .finish(); + Ok(()) + } +} +pub struct NetworkFilterArgs<'a> { + pub mask: u32, + pub opt_domains: Option>>, + pub opt_not_domains: Option>>, + pub patterns: Option>>>, + pub modifier_option: Option>, + pub hostname: Option>, + pub tag: Option>, + pub raw_line: Option>, +} +impl<'a> Default for NetworkFilterArgs<'a> { + #[inline] + fn default() -> Self { + NetworkFilterArgs { + mask: 0, + opt_domains: None, + opt_not_domains: None, + patterns: None, + modifier_option: None, + hostname: None, + tag: None, + raw_line: None, } + } +} - impl<'a> flatbuffers::Follow<'a> for NetworkFilterList<'a> { - type Inner = NetworkFilterList<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { - _tab: flatbuffers::Table::new(buf, loc), - } - } +pub struct NetworkFilterBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterBuilder<'a, 'b, A> { + #[inline] + pub fn add_mask(&mut self, mask: u32) { + self.fbb_.push_slot::(NetworkFilter::VT_MASK, mask, 0); + } + #[inline] + pub fn add_opt_domains(&mut self, opt_domains: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_OPT_DOMAINS, opt_domains); + } + #[inline] + pub fn add_opt_not_domains(&mut self, opt_not_domains: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_OPT_NOT_DOMAINS, opt_not_domains); + } + #[inline] + pub fn add_patterns(&mut self, patterns: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_PATTERNS, patterns); + } + #[inline] + pub fn add_modifier_option(&mut self, modifier_option: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_MODIFIER_OPTION, modifier_option); + } + #[inline] + pub fn add_hostname(&mut self, hostname: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_HOSTNAME, hostname); + } + #[inline] + pub fn add_tag(&mut self, tag: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_TAG, tag); + } + #[inline] + pub fn add_raw_line(&mut self, raw_line: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>(NetworkFilter::VT_RAW_LINE, raw_line); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> NetworkFilterBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NetworkFilterBuilder { + fbb_: _fbb, + start_: start, } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} - impl<'a> NetworkFilterList<'a> { - pub const VT_FILTER_MAP_INDEX: flatbuffers::VOffsetT = 4; - pub const VT_FILTER_MAP_VALUES: flatbuffers::VOffsetT = 6; - - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - NetworkFilterList { _tab: table } - } - #[allow(unused_mut)] - pub fn create< - 'bldr: 'args, - 'args: 'mut_bldr, - 'mut_bldr, - A: flatbuffers::Allocator + 'bldr, - >( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args NetworkFilterListArgs<'args>, - ) -> flatbuffers::WIPOffset> { - let mut builder = NetworkFilterListBuilder::new(_fbb); - if let Some(x) = args.filter_map_values { - builder.add_filter_map_values(x); - } - if let Some(x) = args.filter_map_index { - builder.add_filter_map_index(x); - } - builder.finish() - } - - pub fn unpack(&self) -> NetworkFilterListT { - let filter_map_index = { - let x = self.filter_map_index(); - x.into_iter().collect() - }; - let filter_map_values = { - let x = self.filter_map_values(); - x.iter().map(|t| t.unpack()).collect() - }; - NetworkFilterListT { - filter_map_index, - filter_map_values, - } - } - - #[inline] - pub fn filter_map_index(&self) -> flatbuffers::Vector<'a, u32> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>>( - NetworkFilterList::VT_FILTER_MAP_INDEX, - None, - ) - .unwrap() - } - } - #[inline] - pub fn filter_map_values( - &self, - ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>, - >>(NetworkFilterList::VT_FILTER_MAP_VALUES, None) - .unwrap() - } - } +impl core::fmt::Debug for NetworkFilter<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("NetworkFilter"); + ds.field("mask", &self.mask()); + ds.field("opt_domains", &self.opt_domains()); + ds.field("opt_not_domains", &self.opt_not_domains()); + ds.field("patterns", &self.patterns()); + ds.field("modifier_option", &self.modifier_option()); + ds.field("hostname", &self.hostname()); + ds.field("tag", &self.tag()); + ds.field("raw_line", &self.raw_line()); + ds.finish() + } +} +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NetworkFilterT { + pub mask: u32, + pub opt_domains: Option>, + pub opt_not_domains: Option>, + pub patterns: Option>, + pub modifier_option: Option, + pub hostname: Option, + pub tag: Option, + pub raw_line: Option, +} +impl Default for NetworkFilterT { + fn default() -> Self { + Self { + mask: 0, + opt_domains: None, + opt_not_domains: None, + patterns: None, + modifier_option: None, + hostname: None, + tag: None, + raw_line: None, } + } +} +impl NetworkFilterT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> + ) -> flatbuffers::WIPOffset> { + let mask = self.mask; + let opt_domains = self.opt_domains.as_ref().map(|x|{ + _fbb.create_vector(x) + }); + let opt_not_domains = self.opt_not_domains.as_ref().map(|x|{ + _fbb.create_vector(x) + }); + let patterns = self.patterns.as_ref().map(|x|{ + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let modifier_option = self.modifier_option.as_ref().map(|x|{ + _fbb.create_string(x) + }); + let hostname = self.hostname.as_ref().map(|x|{ + _fbb.create_string(x) + }); + let tag = self.tag.as_ref().map(|x|{ + _fbb.create_string(x) + }); + let raw_line = self.raw_line.as_ref().map(|x|{ + _fbb.create_string(x) + }); + NetworkFilter::create(_fbb, &NetworkFilterArgs{ + mask, + opt_domains, + opt_not_domains, + patterns, + modifier_option, + hostname, + tag, + raw_line, + }) + } +} +pub enum NetworkFilterListOffset {} +#[derive(Copy, Clone, PartialEq)] - impl flatbuffers::Verifiable for NetworkFilterList<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, - pos: usize, - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>( - "filter_map_index", - Self::VT_FILTER_MAP_INDEX, - true, - )? - .visit_field::>, - >>("filter_map_values", Self::VT_FILTER_MAP_VALUES, true)? - .finish(); - Ok(()) - } - } - pub struct NetworkFilterListArgs<'a> { - pub filter_map_index: Option>>, - pub filter_map_values: Option< - flatbuffers::WIPOffset< - flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, - >, - >, - } - impl<'a> Default for NetworkFilterListArgs<'a> { - #[inline] - fn default() -> Self { - NetworkFilterListArgs { - filter_map_index: None, // required field - filter_map_values: None, // required field - } - } - } +pub struct NetworkFilterList<'a> { + pub _tab: flatbuffers::Table<'a>, +} - pub struct NetworkFilterListBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, - } - impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterListBuilder<'a, 'b, A> { - #[inline] - pub fn add_filter_map_index( - &mut self, - filter_map_index: flatbuffers::WIPOffset>, - ) { - self.fbb_.push_slot_always::>( - NetworkFilterList::VT_FILTER_MAP_INDEX, - filter_map_index, - ); - } - #[inline] - pub fn add_filter_map_values( - &mut self, - filter_map_values: flatbuffers::WIPOffset< - flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, - >, - ) { - self.fbb_.push_slot_always::>( - NetworkFilterList::VT_FILTER_MAP_VALUES, - filter_map_values, - ); - } - #[inline] - pub fn new( - _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - ) -> NetworkFilterListBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - NetworkFilterListBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required( - o, - NetworkFilterList::VT_FILTER_MAP_INDEX, - "filter_map_index", - ); - self.fbb_.required( - o, - NetworkFilterList::VT_FILTER_MAP_VALUES, - "filter_map_values", - ); - flatbuffers::WIPOffset::new(o.value()) - } - } +impl<'a> flatbuffers::Follow<'a> for NetworkFilterList<'a> { + type Inner = NetworkFilterList<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: flatbuffers::Table::new(buf, loc) } + } +} - impl core::fmt::Debug for NetworkFilterList<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("NetworkFilterList"); - ds.field("filter_map_index", &self.filter_map_index()); - ds.field("filter_map_values", &self.filter_map_values()); - ds.finish() - } - } - #[non_exhaustive] - #[derive(Debug, Clone, PartialEq)] - pub struct NetworkFilterListT { - pub filter_map_index: Vec, - pub filter_map_values: Vec, - } - impl Default for NetworkFilterListT { - fn default() -> Self { - Self { - filter_map_index: Default::default(), - filter_map_values: Default::default(), - } - } - } - impl NetworkFilterListT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, - ) -> flatbuffers::WIPOffset> { - let filter_map_index = Some({ - let x = &self.filter_map_index; - _fbb.create_vector(x) - }); - let filter_map_values = Some({ - let x = &self.filter_map_values; - let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); - _fbb.create_vector(&w) - }); - NetworkFilterList::create( - _fbb, - &NetworkFilterListArgs { - filter_map_index, - filter_map_values, - }, - ) - } - } - pub enum EngineOffset {} - #[derive(Copy, Clone, PartialEq)] +impl<'a> NetworkFilterList<'a> { + pub const VT_FILTER_MAP_INDEX: flatbuffers::VOffsetT = 4; + pub const VT_FILTER_MAP_VALUES: flatbuffers::VOffsetT = 6; - pub struct Engine<'a> { - pub _tab: flatbuffers::Table<'a>, - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + NetworkFilterList { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NetworkFilterListArgs<'args> + ) -> flatbuffers::WIPOffset> { + let mut builder = NetworkFilterListBuilder::new(_fbb); + if let Some(x) = args.filter_map_values { builder.add_filter_map_values(x); } + if let Some(x) = args.filter_map_index { builder.add_filter_map_index(x); } + builder.finish() + } - impl<'a> flatbuffers::Follow<'a> for Engine<'a> { - type Inner = Engine<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { - _tab: flatbuffers::Table::new(buf, loc), - } - } + pub fn unpack(&self) -> NetworkFilterListT { + let filter_map_index = { + let x = self.filter_map_index(); + x.into_iter().collect() + }; + let filter_map_values = { + let x = self.filter_map_values(); + x.iter().map(|t| t.unpack()).collect() + }; + NetworkFilterListT { + filter_map_index, + filter_map_values, } + } - impl<'a> Engine<'a> { - pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 4; - pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 6; - - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Engine { _tab: table } - } - #[allow(unused_mut)] - pub fn create< - 'bldr: 'args, - 'args: 'mut_bldr, - 'mut_bldr, - A: flatbuffers::Allocator + 'bldr, - >( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args EngineArgs<'args>, - ) -> flatbuffers::WIPOffset> { - let mut builder = EngineBuilder::new(_fbb); - if let Some(x) = args.unique_domains_hashes { - builder.add_unique_domains_hashes(x); - } - if let Some(x) = args.network_rules { - builder.add_network_rules(x); - } - builder.finish() - } - - pub fn unpack(&self) -> EngineT { - let network_rules = { - let x = self.network_rules(); - x.iter().map(|t| t.unpack()).collect() - }; - let unique_domains_hashes = { - let x = self.unique_domains_hashes(); - x.into_iter().collect() - }; - EngineT { - network_rules, - unique_domains_hashes, - } - } - - #[inline] - pub fn network_rules( - &self, - ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>, - >>(Engine::VT_NETWORK_RULES, None) - .unwrap() - } - } - #[inline] - pub fn unique_domains_hashes(&self) -> flatbuffers::Vector<'a, u64> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::>>( - Engine::VT_UNIQUE_DOMAINS_HASHES, - None, - ) - .unwrap() - } - } - } + #[inline] + pub fn filter_map_index(&self) -> flatbuffers::Vector<'a, u32> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(NetworkFilterList::VT_FILTER_MAP_INDEX, None).unwrap()} + } + #[inline] + pub fn filter_map_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(NetworkFilterList::VT_FILTER_MAP_VALUES, None).unwrap()} + } +} - impl flatbuffers::Verifiable for Engine<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, - pos: usize, - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>, - >>("network_rules", Self::VT_NETWORK_RULES, true)? - .visit_field::>>( - "unique_domains_hashes", - Self::VT_UNIQUE_DOMAINS_HASHES, - true, - )? - .finish(); - Ok(()) - } - } - pub struct EngineArgs<'a> { - pub network_rules: Option< - flatbuffers::WIPOffset< - flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, - >, - >, - pub unique_domains_hashes: Option>>, - } - impl<'a> Default for EngineArgs<'a> { - #[inline] - fn default() -> Self { - EngineArgs { - network_rules: None, // required field - unique_domains_hashes: None, // required field - } - } +impl flatbuffers::Verifiable for NetworkFilterList<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>("filter_map_index", Self::VT_FILTER_MAP_INDEX, true)? + .visit_field::>>>("filter_map_values", Self::VT_FILTER_MAP_VALUES, true)? + .finish(); + Ok(()) + } +} +pub struct NetworkFilterListArgs<'a> { + pub filter_map_index: Option>>, + pub filter_map_values: Option>>>>, +} +impl<'a> Default for NetworkFilterListArgs<'a> { + #[inline] + fn default() -> Self { + NetworkFilterListArgs { + filter_map_index: None, // required field + filter_map_values: None, // required field } + } +} - pub struct EngineBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, +pub struct NetworkFilterListBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterListBuilder<'a, 'b, A> { + #[inline] + pub fn add_filter_map_index(&mut self, filter_map_index: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(NetworkFilterList::VT_FILTER_MAP_INDEX, filter_map_index); + } + #[inline] + pub fn add_filter_map_values(&mut self, filter_map_values: flatbuffers::WIPOffset>>>) { + self.fbb_.push_slot_always::>(NetworkFilterList::VT_FILTER_MAP_VALUES, filter_map_values); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> NetworkFilterListBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NetworkFilterListBuilder { + fbb_: _fbb, + start_: start, } - impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EngineBuilder<'a, 'b, A> { - #[inline] - pub fn add_network_rules( - &mut self, - network_rules: flatbuffers::WIPOffset< - flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, - >, - ) { - self.fbb_.push_slot_always::>( - Engine::VT_NETWORK_RULES, - network_rules, - ); - } - #[inline] - pub fn add_unique_domains_hashes( - &mut self, - unique_domains_hashes: flatbuffers::WIPOffset>, - ) { - self.fbb_.push_slot_always::>( - Engine::VT_UNIQUE_DOMAINS_HASHES, - unique_domains_hashes, - ); - } - #[inline] - pub fn new( - _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - ) -> EngineBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - EngineBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_ - .required(o, Engine::VT_NETWORK_RULES, "network_rules"); - self.fbb_ - .required(o, Engine::VT_UNIQUE_DOMAINS_HASHES, "unique_domains_hashes"); - flatbuffers::WIPOffset::new(o.value()) - } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, NetworkFilterList::VT_FILTER_MAP_INDEX,"filter_map_index"); + self.fbb_.required(o, NetworkFilterList::VT_FILTER_MAP_VALUES,"filter_map_values"); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for NetworkFilterList<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("NetworkFilterList"); + ds.field("filter_map_index", &self.filter_map_index()); + ds.field("filter_map_values", &self.filter_map_values()); + ds.finish() + } +} +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NetworkFilterListT { + pub filter_map_index: Vec, + pub filter_map_values: Vec, +} +impl Default for NetworkFilterListT { + fn default() -> Self { + Self { + filter_map_index: Default::default(), + filter_map_values: Default::default(), } + } +} +impl NetworkFilterListT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> + ) -> flatbuffers::WIPOffset> { + let filter_map_index = Some({ + let x = &self.filter_map_index; + _fbb.create_vector(x) + }); + let filter_map_values = Some({ + let x = &self.filter_map_values; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) + }); + NetworkFilterList::create(_fbb, &NetworkFilterListArgs{ + filter_map_index, + filter_map_values, + }) + } +} +pub enum HostnameSpecificRulesOffset {} +#[derive(Copy, Clone, PartialEq)] - impl core::fmt::Debug for Engine<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Engine"); - ds.field("network_rules", &self.network_rules()); - ds.field("unique_domains_hashes", &self.unique_domains_hashes()); - ds.finish() - } +pub struct HostnameSpecificRules<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for HostnameSpecificRules<'a> { + type Inner = HostnameSpecificRules<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: flatbuffers::Table::new(buf, loc) } + } +} + +impl<'a> HostnameSpecificRules<'a> { + pub const VT_UNHIDE: flatbuffers::VOffsetT = 4; + pub const VT_UNINJECT_SCRIPT: flatbuffers::VOffsetT = 6; + pub const VT_PROCEDURAL_ACTION: flatbuffers::VOffsetT = 8; + pub const VT_PROCEDURAL_ACTION_EXCEPTION: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + HostnameSpecificRules { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args HostnameSpecificRulesArgs<'args> + ) -> flatbuffers::WIPOffset> { + let mut builder = HostnameSpecificRulesBuilder::new(_fbb); + if let Some(x) = args.procedural_action_exception { builder.add_procedural_action_exception(x); } + if let Some(x) = args.procedural_action { builder.add_procedural_action(x); } + if let Some(x) = args.uninject_script { builder.add_uninject_script(x); } + if let Some(x) = args.unhide { builder.add_unhide(x); } + builder.finish() + } + + pub fn unpack(&self) -> HostnameSpecificRulesT { + let unhide = self.unhide().map(|x| { + x.iter().map(|s| s.to_string()).collect() + }); + let uninject_script = self.uninject_script().map(|x| { + x.iter().map(|s| s.to_string()).collect() + }); + let procedural_action = self.procedural_action().map(|x| { + x.iter().map(|s| s.to_string()).collect() + }); + let procedural_action_exception = self.procedural_action_exception().map(|x| { + x.iter().map(|s| s.to_string()).collect() + }); + HostnameSpecificRulesT { + unhide, + uninject_script, + procedural_action, + procedural_action_exception, } - #[non_exhaustive] - #[derive(Debug, Clone, PartialEq)] - pub struct EngineT { - pub network_rules: Vec, - pub unique_domains_hashes: Vec, + } + + #[inline] + pub fn unhide(&self) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_UNHIDE, None)} + } + #[inline] + pub fn uninject_script(&self) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, None)} + } + #[inline] + pub fn procedural_action(&self) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, None)} + } + #[inline] + pub fn procedural_action_exception(&self) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, None)} + } +} + +impl flatbuffers::Verifiable for HostnameSpecificRules<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>>("unhide", Self::VT_UNHIDE, false)? + .visit_field::>>>("uninject_script", Self::VT_UNINJECT_SCRIPT, false)? + .visit_field::>>>("procedural_action", Self::VT_PROCEDURAL_ACTION, false)? + .visit_field::>>>("procedural_action_exception", Self::VT_PROCEDURAL_ACTION_EXCEPTION, false)? + .finish(); + Ok(()) + } +} +pub struct HostnameSpecificRulesArgs<'a> { + pub unhide: Option>>>, + pub uninject_script: Option>>>, + pub procedural_action: Option>>>, + pub procedural_action_exception: Option>>>, +} +impl<'a> Default for HostnameSpecificRulesArgs<'a> { + #[inline] + fn default() -> Self { + HostnameSpecificRulesArgs { + unhide: None, + uninject_script: None, + procedural_action: None, + procedural_action_exception: None, } - impl Default for EngineT { - fn default() -> Self { - Self { - network_rules: Default::default(), - unique_domains_hashes: Default::default(), - } - } + } +} + +pub struct HostnameSpecificRulesBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> HostnameSpecificRulesBuilder<'a, 'b, A> { + #[inline] + pub fn add_unhide(&mut self, unhide: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_UNHIDE, unhide); + } + #[inline] + pub fn add_uninject_script(&mut self, uninject_script: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, uninject_script); + } + #[inline] + pub fn add_procedural_action(&mut self, procedural_action: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, procedural_action); + } + #[inline] + pub fn add_procedural_action_exception(&mut self, procedural_action_exception: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, procedural_action_exception); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> HostnameSpecificRulesBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + HostnameSpecificRulesBuilder { + fbb_: _fbb, + start_: start, } - impl EngineT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, - ) -> flatbuffers::WIPOffset> { - let network_rules = Some({ - let x = &self.network_rules; - let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); - _fbb.create_vector(&w) - }); - let unique_domains_hashes = Some({ - let x = &self.unique_domains_hashes; - _fbb.create_vector(x) - }); - Engine::create( - _fbb, - &EngineArgs { - network_rules, - unique_domains_hashes, - }, - ) - } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for HostnameSpecificRules<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("HostnameSpecificRules"); + ds.field("unhide", &self.unhide()); + ds.field("uninject_script", &self.uninject_script()); + ds.field("procedural_action", &self.procedural_action()); + ds.field("procedural_action_exception", &self.procedural_action_exception()); + ds.finish() + } +} +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct HostnameSpecificRulesT { + pub unhide: Option>, + pub uninject_script: Option>, + pub procedural_action: Option>, + pub procedural_action_exception: Option>, +} +impl Default for HostnameSpecificRulesT { + fn default() -> Self { + Self { + unhide: None, + uninject_script: None, + procedural_action: None, + procedural_action_exception: None, } - #[inline] - /// Verifies that a buffer of bytes contains a `Engine` - /// and returns it. - /// Note that verification is still experimental and may not - /// catch every error, or be maximally performant. For the - /// previous, unchecked, behavior use - /// `root_as_engine_unchecked`. - pub fn root_as_engine(buf: &[u8]) -> Result { - flatbuffers::root::(buf) + } +} +impl HostnameSpecificRulesT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> + ) -> flatbuffers::WIPOffset> { + let unhide = self.unhide.as_ref().map(|x|{ + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let uninject_script = self.uninject_script.as_ref().map(|x|{ + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let procedural_action = self.procedural_action.as_ref().map(|x|{ + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let procedural_action_exception = self.procedural_action_exception.as_ref().map(|x|{ + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + HostnameSpecificRules::create(_fbb, &HostnameSpecificRulesArgs{ + unhide, + uninject_script, + procedural_action, + procedural_action_exception, + }) + } +} +pub enum CosmeticFiltersOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct CosmeticFilters<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for CosmeticFilters<'a> { + type Inner = CosmeticFilters<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: flatbuffers::Table::new(buf, loc) } + } +} + +impl<'a> CosmeticFilters<'a> { + pub const VT_SIMPLE_CLASS_RULES: flatbuffers::VOffsetT = 4; + pub const VT_SIMPLE_ID_RULES: flatbuffers::VOffsetT = 6; + pub const VT_MISC_GENERIC_SELECTORS: flatbuffers::VOffsetT = 8; + pub const VT_COMPLEX_CLASS_RULES_INDEX: flatbuffers::VOffsetT = 10; + pub const VT_COMPLEX_CLASS_RULES_VALUES: flatbuffers::VOffsetT = 12; + pub const VT_COMPLEX_ID_RULES_INDEX: flatbuffers::VOffsetT = 14; + pub const VT_COMPLEX_ID_RULES_VALUES: flatbuffers::VOffsetT = 16; + pub const VT_HOSTNAME_HIDE_INDEX: flatbuffers::VOffsetT = 18; + pub const VT_HOSTNAME_HIDE_VALUES: flatbuffers::VOffsetT = 20; + pub const VT_HOSTNAME_INJECT_SCRIPT_INDEX: flatbuffers::VOffsetT = 22; + pub const VT_HOSTNAME_INJECT_SCRIPT_VALUES: flatbuffers::VOffsetT = 24; + pub const VT_HOSTNAME_INDEX: flatbuffers::VOffsetT = 26; + pub const VT_HOSTNAME_VALUES: flatbuffers::VOffsetT = 28; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + CosmeticFilters { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CosmeticFiltersArgs<'args> + ) -> flatbuffers::WIPOffset> { + let mut builder = CosmeticFiltersBuilder::new(_fbb); + if let Some(x) = args.hostname_values { builder.add_hostname_values(x); } + if let Some(x) = args.hostname_index { builder.add_hostname_index(x); } + if let Some(x) = args.hostname_inject_script_values { builder.add_hostname_inject_script_values(x); } + if let Some(x) = args.hostname_inject_script_index { builder.add_hostname_inject_script_index(x); } + if let Some(x) = args.hostname_hide_values { builder.add_hostname_hide_values(x); } + if let Some(x) = args.hostname_hide_index { builder.add_hostname_hide_index(x); } + if let Some(x) = args.complex_id_rules_values { builder.add_complex_id_rules_values(x); } + if let Some(x) = args.complex_id_rules_index { builder.add_complex_id_rules_index(x); } + if let Some(x) = args.complex_class_rules_values { builder.add_complex_class_rules_values(x); } + if let Some(x) = args.complex_class_rules_index { builder.add_complex_class_rules_index(x); } + if let Some(x) = args.misc_generic_selectors { builder.add_misc_generic_selectors(x); } + if let Some(x) = args.simple_id_rules { builder.add_simple_id_rules(x); } + if let Some(x) = args.simple_class_rules { builder.add_simple_class_rules(x); } + builder.finish() + } + + pub fn unpack(&self) -> CosmeticFiltersT { + let simple_class_rules = { + let x = self.simple_class_rules(); + x.iter().map(|s| s.to_string()).collect() + }; + let simple_id_rules = { + let x = self.simple_id_rules(); + x.iter().map(|s| s.to_string()).collect() + }; + let misc_generic_selectors = { + let x = self.misc_generic_selectors(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_class_rules_index = { + let x = self.complex_class_rules_index(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_class_rules_values = { + let x = self.complex_class_rules_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_id_rules_index = { + let x = self.complex_id_rules_index(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_id_rules_values = { + let x = self.complex_id_rules_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_hide_index = { + let x = self.hostname_hide_index(); + x.into_iter().collect() + }; + let hostname_hide_values = { + let x = self.hostname_hide_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_inject_script_index = { + let x = self.hostname_inject_script_index(); + x.into_iter().collect() + }; + let hostname_inject_script_values = { + let x = self.hostname_inject_script_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_index = { + let x = self.hostname_index(); + x.into_iter().collect() + }; + let hostname_values = { + let x = self.hostname_values(); + x.iter().map(|t| t.unpack()).collect() + }; + CosmeticFiltersT { + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index, + complex_class_rules_values, + complex_id_rules_index, + complex_id_rules_values, + hostname_hide_index, + hostname_hide_values, + hostname_inject_script_index, + hostname_inject_script_values, + hostname_index, + hostname_values, } - #[inline] - /// Verifies that a buffer of bytes contains a size prefixed - /// `Engine` and returns it. - /// Note that verification is still experimental and may not - /// catch every error, or be maximally performant. For the - /// previous, unchecked, behavior use - /// `size_prefixed_root_as_engine_unchecked`. - pub fn size_prefixed_root_as_engine( - buf: &[u8], - ) -> Result { - flatbuffers::size_prefixed_root::(buf) + } + + /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. + #[inline] + pub fn simple_class_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_SIMPLE_CLASS_RULES, None).unwrap()} + } + /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. + #[inline] + pub fn simple_id_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_SIMPLE_ID_RULES, None).unwrap()} + } + /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit + /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` + #[inline] + pub fn misc_generic_selectors(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_MISC_GENERIC_SELECTORS, None).unwrap()} + } + /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` + /// These are stored as a multi-map from class name to list of selectors + #[inline] + pub fn complex_class_rules_index(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, None).unwrap()} + } + #[inline] + pub fn complex_class_rules_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, None).unwrap()} + } + /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` + /// These are stored as a multi-map from id name to list of selectors + #[inline] + pub fn complex_id_rules_index(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, None).unwrap()} + } + #[inline] + pub fn complex_id_rules_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, None).unwrap()} + } + /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors + #[inline] + pub fn hostname_hide_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, None).unwrap()} + } + #[inline] + pub fn hostname_hide_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, None).unwrap()} + } + /// Hostname-specific script injection filters - multi-map from hostname hash to script data + /// First byte of each script encodes permission bits to avoid separate permissions array + #[inline] + pub fn hostname_inject_script_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, None).unwrap()} + } + #[inline] + pub fn hostname_inject_script_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, None).unwrap()} + } + #[inline] + pub fn hostname_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_INDEX, None).unwrap()} + } + #[inline] + pub fn hostname_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_VALUES, None).unwrap()} + } +} + +impl flatbuffers::Verifiable for CosmeticFilters<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>>("simple_class_rules", Self::VT_SIMPLE_CLASS_RULES, true)? + .visit_field::>>>("simple_id_rules", Self::VT_SIMPLE_ID_RULES, true)? + .visit_field::>>>("misc_generic_selectors", Self::VT_MISC_GENERIC_SELECTORS, true)? + .visit_field::>>>("complex_class_rules_index", Self::VT_COMPLEX_CLASS_RULES_INDEX, true)? + .visit_field::>>>("complex_class_rules_values", Self::VT_COMPLEX_CLASS_RULES_VALUES, true)? + .visit_field::>>>("complex_id_rules_index", Self::VT_COMPLEX_ID_RULES_INDEX, true)? + .visit_field::>>>("complex_id_rules_values", Self::VT_COMPLEX_ID_RULES_VALUES, true)? + .visit_field::>>("hostname_hide_index", Self::VT_HOSTNAME_HIDE_INDEX, true)? + .visit_field::>>>("hostname_hide_values", Self::VT_HOSTNAME_HIDE_VALUES, true)? + .visit_field::>>("hostname_inject_script_index", Self::VT_HOSTNAME_INJECT_SCRIPT_INDEX, true)? + .visit_field::>>>("hostname_inject_script_values", Self::VT_HOSTNAME_INJECT_SCRIPT_VALUES, true)? + .visit_field::>>("hostname_index", Self::VT_HOSTNAME_INDEX, true)? + .visit_field::>>>("hostname_values", Self::VT_HOSTNAME_VALUES, true)? + .finish(); + Ok(()) + } +} +pub struct CosmeticFiltersArgs<'a> { + pub simple_class_rules: Option>>>, + pub simple_id_rules: Option>>>, + pub misc_generic_selectors: Option>>>, + pub complex_class_rules_index: Option>>>, + pub complex_class_rules_values: Option>>>, + pub complex_id_rules_index: Option>>>, + pub complex_id_rules_values: Option>>>, + pub hostname_hide_index: Option>>, + pub hostname_hide_values: Option>>>, + pub hostname_inject_script_index: Option>>, + pub hostname_inject_script_values: Option>>>, + pub hostname_index: Option>>, + pub hostname_values: Option>>>>, +} +impl<'a> Default for CosmeticFiltersArgs<'a> { + #[inline] + fn default() -> Self { + CosmeticFiltersArgs { + simple_class_rules: None, // required field + simple_id_rules: None, // required field + misc_generic_selectors: None, // required field + complex_class_rules_index: None, // required field + complex_class_rules_values: None, // required field + complex_id_rules_index: None, // required field + complex_id_rules_values: None, // required field + hostname_hide_index: None, // required field + hostname_hide_values: None, // required field + hostname_inject_script_index: None, // required field + hostname_inject_script_values: None, // required field + hostname_index: None, // required field + hostname_values: None, // required field } - #[inline] - /// Verifies, with the given options, that a buffer of bytes - /// contains a `Engine` and returns it. - /// Note that verification is still experimental and may not - /// catch every error, or be maximally performant. For the - /// previous, unchecked, behavior use - /// `root_as_engine_unchecked`. - pub fn root_as_engine_with_opts<'b, 'o>( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], - ) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::root_with_opts::>(opts, buf) + } +} + +pub struct CosmeticFiltersBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> CosmeticFiltersBuilder<'a, 'b, A> { + #[inline] + pub fn add_simple_class_rules(&mut self, simple_class_rules: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_SIMPLE_CLASS_RULES, simple_class_rules); + } + #[inline] + pub fn add_simple_id_rules(&mut self, simple_id_rules: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_SIMPLE_ID_RULES, simple_id_rules); + } + #[inline] + pub fn add_misc_generic_selectors(&mut self, misc_generic_selectors: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_MISC_GENERIC_SELECTORS, misc_generic_selectors); + } + #[inline] + pub fn add_complex_class_rules_index(&mut self, complex_class_rules_index: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, complex_class_rules_index); + } + #[inline] + pub fn add_complex_class_rules_values(&mut self, complex_class_rules_values: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, complex_class_rules_values); + } + #[inline] + pub fn add_complex_id_rules_index(&mut self, complex_id_rules_index: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, complex_id_rules_index); + } + #[inline] + pub fn add_complex_id_rules_values(&mut self, complex_id_rules_values: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, complex_id_rules_values); + } + #[inline] + pub fn add_hostname_hide_index(&mut self, hostname_hide_index: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, hostname_hide_index); + } + #[inline] + pub fn add_hostname_hide_values(&mut self, hostname_hide_values: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, hostname_hide_values); + } + #[inline] + pub fn add_hostname_inject_script_index(&mut self, hostname_inject_script_index: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, hostname_inject_script_index); + } + #[inline] + pub fn add_hostname_inject_script_values(&mut self, hostname_inject_script_values: flatbuffers::WIPOffset>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, hostname_inject_script_values); + } + #[inline] + pub fn add_hostname_index(&mut self, hostname_index: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INDEX, hostname_index); + } + #[inline] + pub fn add_hostname_values(&mut self, hostname_values: flatbuffers::WIPOffset>>>) { + self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_VALUES, hostname_values); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> CosmeticFiltersBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CosmeticFiltersBuilder { + fbb_: _fbb, + start_: start, } - #[inline] - /// Verifies, with the given verifier options, that a buffer of - /// bytes contains a size prefixed `Engine` and returns - /// it. Note that verification is still experimental and may not - /// catch every error, or be maximally performant. For the - /// previous, unchecked, behavior use - /// `root_as_engine_unchecked`. - pub fn size_prefixed_root_as_engine_with_opts<'b, 'o>( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], - ) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::size_prefixed_root_with_opts::>(opts, buf) + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, CosmeticFilters::VT_SIMPLE_CLASS_RULES,"simple_class_rules"); + self.fbb_.required(o, CosmeticFilters::VT_SIMPLE_ID_RULES,"simple_id_rules"); + self.fbb_.required(o, CosmeticFilters::VT_MISC_GENERIC_SELECTORS,"misc_generic_selectors"); + self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX,"complex_class_rules_index"); + self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES,"complex_class_rules_values"); + self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX,"complex_id_rules_index"); + self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES,"complex_id_rules_values"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_HIDE_INDEX,"hostname_hide_index"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_HIDE_VALUES,"hostname_hide_values"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX,"hostname_inject_script_index"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES,"hostname_inject_script_values"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INDEX,"hostname_index"); + self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_VALUES,"hostname_values"); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for CosmeticFilters<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("CosmeticFilters"); + ds.field("simple_class_rules", &self.simple_class_rules()); + ds.field("simple_id_rules", &self.simple_id_rules()); + ds.field("misc_generic_selectors", &self.misc_generic_selectors()); + ds.field("complex_class_rules_index", &self.complex_class_rules_index()); + ds.field("complex_class_rules_values", &self.complex_class_rules_values()); + ds.field("complex_id_rules_index", &self.complex_id_rules_index()); + ds.field("complex_id_rules_values", &self.complex_id_rules_values()); + ds.field("hostname_hide_index", &self.hostname_hide_index()); + ds.field("hostname_hide_values", &self.hostname_hide_values()); + ds.field("hostname_inject_script_index", &self.hostname_inject_script_index()); + ds.field("hostname_inject_script_values", &self.hostname_inject_script_values()); + ds.field("hostname_index", &self.hostname_index()); + ds.field("hostname_values", &self.hostname_values()); + ds.finish() + } +} +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct CosmeticFiltersT { + pub simple_class_rules: Vec, + pub simple_id_rules: Vec, + pub misc_generic_selectors: Vec, + pub complex_class_rules_index: Vec, + pub complex_class_rules_values: Vec, + pub complex_id_rules_index: Vec, + pub complex_id_rules_values: Vec, + pub hostname_hide_index: Vec, + pub hostname_hide_values: Vec, + pub hostname_inject_script_index: Vec, + pub hostname_inject_script_values: Vec, + pub hostname_index: Vec, + pub hostname_values: Vec, +} +impl Default for CosmeticFiltersT { + fn default() -> Self { + Self { + simple_class_rules: Default::default(), + simple_id_rules: Default::default(), + misc_generic_selectors: Default::default(), + complex_class_rules_index: Default::default(), + complex_class_rules_values: Default::default(), + complex_id_rules_index: Default::default(), + complex_id_rules_values: Default::default(), + hostname_hide_index: Default::default(), + hostname_hide_values: Default::default(), + hostname_inject_script_index: Default::default(), + hostname_inject_script_values: Default::default(), + hostname_index: Default::default(), + hostname_values: Default::default(), } - #[inline] - /// Assumes, without verification, that a buffer of bytes contains a Engine and returns it. - /// # Safety - /// Callers must trust the given bytes do indeed contain a valid `Engine`. - pub unsafe fn root_as_engine_unchecked(buf: &[u8]) -> Engine { - flatbuffers::root_unchecked::(buf) + } +} +impl CosmeticFiltersT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> + ) -> flatbuffers::WIPOffset> { + let simple_class_rules = Some({ + let x = &self.simple_class_rules; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let simple_id_rules = Some({ + let x = &self.simple_id_rules; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let misc_generic_selectors = Some({ + let x = &self.misc_generic_selectors; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let complex_class_rules_index = Some({ + let x = &self.complex_class_rules_index; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let complex_class_rules_values = Some({ + let x = &self.complex_class_rules_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let complex_id_rules_index = Some({ + let x = &self.complex_id_rules_index; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let complex_id_rules_values = Some({ + let x = &self.complex_id_rules_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let hostname_hide_index = Some({ + let x = &self.hostname_hide_index; + _fbb.create_vector(x) + }); + let hostname_hide_values = Some({ + let x = &self.hostname_hide_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let hostname_inject_script_index = Some({ + let x = &self.hostname_inject_script_index; + _fbb.create_vector(x) + }); + let hostname_inject_script_values = Some({ + let x = &self.hostname_inject_script_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) + }); + let hostname_index = Some({ + let x = &self.hostname_index; + _fbb.create_vector(x) + }); + let hostname_values = Some({ + let x = &self.hostname_values; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) + }); + CosmeticFilters::create(_fbb, &CosmeticFiltersArgs{ + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index, + complex_class_rules_values, + complex_id_rules_index, + complex_id_rules_values, + hostname_hide_index, + hostname_hide_values, + hostname_inject_script_index, + hostname_inject_script_values, + hostname_index, + hostname_values, + }) + } +} +pub enum EngineOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct Engine<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for Engine<'a> { + type Inner = Engine<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { _tab: flatbuffers::Table::new(buf, loc) } + } +} + +impl<'a> Engine<'a> { + pub const VT_VERSION: flatbuffers::VOffsetT = 4; + pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 6; + pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 8; + pub const VT_COSMETIC_FILTERS: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Engine { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EngineArgs<'args> + ) -> flatbuffers::WIPOffset> { + let mut builder = EngineBuilder::new(_fbb); + if let Some(x) = args.cosmetic_filters { builder.add_cosmetic_filters(x); } + if let Some(x) = args.unique_domains_hashes { builder.add_unique_domains_hashes(x); } + if let Some(x) = args.network_rules { builder.add_network_rules(x); } + builder.add_version(args.version); + builder.finish() + } + + pub fn unpack(&self) -> EngineT { + let version = self.version(); + let network_rules = { + let x = self.network_rules(); + x.iter().map(|t| t.unpack()).collect() + }; + let unique_domains_hashes = { + let x = self.unique_domains_hashes(); + x.into_iter().collect() + }; + let cosmetic_filters = { + let x = self.cosmetic_filters(); + Box::new(x.unpack()) + }; + EngineT { + version, + network_rules, + unique_domains_hashes, + cosmetic_filters, } - #[inline] - /// Assumes, without verification, that a buffer of bytes contains a size prefixed Engine and returns it. - /// # Safety - /// Callers must trust the given bytes do indeed contain a valid size prefixed `Engine`. - pub unsafe fn size_prefixed_root_as_engine_unchecked(buf: &[u8]) -> Engine { - flatbuffers::size_prefixed_root_unchecked::(buf) + } + + #[inline] + pub fn version(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Engine::VT_VERSION, Some(0)).unwrap()} + } + #[inline] + pub fn network_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>>(Engine::VT_NETWORK_RULES, None).unwrap()} + } + #[inline] + pub fn unique_domains_hashes(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>>(Engine::VT_UNIQUE_DOMAINS_HASHES, None).unwrap()} + } + #[inline] + pub fn cosmetic_filters(&self) -> CosmeticFilters<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::>(Engine::VT_COSMETIC_FILTERS, None).unwrap()} + } +} + +impl flatbuffers::Verifiable for Engine<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, pos: usize + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("version", Self::VT_VERSION, false)? + .visit_field::>>>("network_rules", Self::VT_NETWORK_RULES, true)? + .visit_field::>>("unique_domains_hashes", Self::VT_UNIQUE_DOMAINS_HASHES, true)? + .visit_field::>("cosmetic_filters", Self::VT_COSMETIC_FILTERS, true)? + .finish(); + Ok(()) + } +} +pub struct EngineArgs<'a> { + pub version: u32, + pub network_rules: Option>>>>, + pub unique_domains_hashes: Option>>, + pub cosmetic_filters: Option>>, +} +impl<'a> Default for EngineArgs<'a> { + #[inline] + fn default() -> Self { + EngineArgs { + version: 0, + network_rules: None, // required field + unique_domains_hashes: None, // required field + cosmetic_filters: None, // required field } - #[inline] - pub fn finish_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( - fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - root: flatbuffers::WIPOffset>, - ) { - fbb.finish(root, None); + } +} + +pub struct EngineBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EngineBuilder<'a, 'b, A> { + #[inline] + pub fn add_version(&mut self, version: u32) { + self.fbb_.push_slot::(Engine::VT_VERSION, version, 0); + } + #[inline] + pub fn add_network_rules(&mut self, network_rules: flatbuffers::WIPOffset>>>) { + self.fbb_.push_slot_always::>(Engine::VT_NETWORK_RULES, network_rules); + } + #[inline] + pub fn add_unique_domains_hashes(&mut self, unique_domains_hashes: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(Engine::VT_UNIQUE_DOMAINS_HASHES, unique_domains_hashes); + } + #[inline] + pub fn add_cosmetic_filters(&mut self, cosmetic_filters: flatbuffers::WIPOffset>) { + self.fbb_.push_slot_always::>(Engine::VT_COSMETIC_FILTERS, cosmetic_filters); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> EngineBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EngineBuilder { + fbb_: _fbb, + start_: start, } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Engine::VT_NETWORK_RULES,"network_rules"); + self.fbb_.required(o, Engine::VT_UNIQUE_DOMAINS_HASHES,"unique_domains_hashes"); + self.fbb_.required(o, Engine::VT_COSMETIC_FILTERS,"cosmetic_filters"); + flatbuffers::WIPOffset::new(o.value()) + } +} - #[inline] - pub fn finish_size_prefixed_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( - fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - root: flatbuffers::WIPOffset>, - ) { - fbb.finish_size_prefixed(root, None); +impl core::fmt::Debug for Engine<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Engine"); + ds.field("version", &self.version()); + ds.field("network_rules", &self.network_rules()); + ds.field("unique_domains_hashes", &self.unique_domains_hashes()); + ds.field("cosmetic_filters", &self.cosmetic_filters()); + ds.finish() + } +} +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct EngineT { + pub version: u32, + pub network_rules: Vec, + pub unique_domains_hashes: Vec, + pub cosmetic_filters: Box, +} +impl Default for EngineT { + fn default() -> Self { + Self { + version: 0, + network_rules: Default::default(), + unique_domains_hashes: Default::default(), + cosmetic_filters: Default::default(), } -} // pub mod fb + } +} +impl EngineT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> + ) -> flatbuffers::WIPOffset> { + let version = self.version; + let network_rules = Some({ + let x = &self.network_rules; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) + }); + let unique_domains_hashes = Some({ + let x = &self.unique_domains_hashes; + _fbb.create_vector(x) + }); + let cosmetic_filters = Some({ + let x = &self.cosmetic_filters; + x.pack(_fbb) + }); + Engine::create(_fbb, &EngineArgs{ + version, + network_rules, + unique_domains_hashes, + cosmetic_filters, + }) + } +} +#[inline] +/// Verifies that a buffer of bytes contains a `Engine` +/// and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_engine_unchecked`. +pub fn root_as_engine(buf: &[u8]) -> Result { + flatbuffers::root::(buf) +} +#[inline] +/// Verifies that a buffer of bytes contains a size prefixed +/// `Engine` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `size_prefixed_root_as_engine_unchecked`. +pub fn size_prefixed_root_as_engine(buf: &[u8]) -> Result { + flatbuffers::size_prefixed_root::(buf) +} +#[inline] +/// Verifies, with the given options, that a buffer of bytes +/// contains a `Engine` and returns it. +/// Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_engine_unchecked`. +pub fn root_as_engine_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::>(opts, buf) +} +#[inline] +/// Verifies, with the given verifier options, that a buffer of +/// bytes contains a size prefixed `Engine` and returns +/// it. Note that verification is still experimental and may not +/// catch every error, or be maximally performant. For the +/// previous, unchecked, behavior use +/// `root_as_engine_unchecked`. +pub fn size_prefixed_root_as_engine_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], +) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::>(opts, buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a Engine and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid `Engine`. +pub unsafe fn root_as_engine_unchecked(buf: &[u8]) -> Engine { + flatbuffers::root_unchecked::(buf) +} +#[inline] +/// Assumes, without verification, that a buffer of bytes contains a size prefixed Engine and returns it. +/// # Safety +/// Callers must trust the given bytes do indeed contain a valid size prefixed `Engine`. +pub unsafe fn size_prefixed_root_as_engine_unchecked(buf: &[u8]) -> Engine { + flatbuffers::size_prefixed_root_unchecked::(buf) +} +#[inline] +pub fn finish_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + root: flatbuffers::WIPOffset>) { + fbb.finish(root, None); +} + +#[inline] +pub fn finish_size_prefixed_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { + fbb.finish_size_prefixed(root, None); +} +} // pub mod fb + diff --git a/src/lib.rs b/src/lib.rs index d6327d2d..0d95e5f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,6 @@ pub mod blocker; #[cfg(feature = "content-blocking")] pub mod content_blocking; pub mod cosmetic_filter_cache; -mod data_format; mod engine; pub mod filters; mod flatbuffers; diff --git a/src/network_filter_list.rs b/src/network_filter_list.rs index b4d1c761..80b30685 100644 --- a/src/network_filter_list.rs +++ b/src/network_filter_list.rs @@ -50,12 +50,6 @@ impl NetworkFilterMaskHelper for CheckResult { } } -#[derive(Debug, Clone)] -pub enum NetworkFilterListParsingError { - InvalidFlatbuffer(flatbuffers::InvalidFlatbuffer), - UniqueDomainsOutOfBounds(usize), -} - /// Internal structure to keep track of a collection of network filters. pub(crate) struct NetworkFilterList<'a> { pub(crate) list: fb::NetworkFilterList<'a>, diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 7fda7af2..eb7c2321 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -101,6 +101,10 @@ impl PermissionMask { Self(bits) } + pub fn to_bits(&self) -> u8 { + self.0 + } + /// Can `filter_mask` authorize injecting a resource requiring `self` permissions? pub fn is_injectable_by(&self, filter_mask: PermissionMask) -> bool { // For any particular bit index, the scriptlet is injectable if: diff --git a/tests/legacy_harness.rs b/tests/legacy_harness.rs index a11d5449..37cf5be8 100644 --- a/tests/legacy_harness.rs +++ b/tests/legacy_harness.rs @@ -330,7 +330,7 @@ mod legacy_check_match { let mut engine_deserialized = Engine::default(); // second empty engine_deserialized.use_tags(tags); { - let engine_serialized = engine.serialize().unwrap(); + let engine_serialized = engine.serialize().to_vec(); engine_deserialized.deserialize(&engine_serialized).unwrap(); // override from serialized copy } @@ -404,7 +404,7 @@ mod legacy_check_match { ); let mut engine_deserialized = Engine::default(); // second empty { - let engine_serialized = engine.serialize().unwrap(); + let engine_serialized = engine.serialize().to_vec(); engine_deserialized.deserialize(&engine_serialized).unwrap(); // override from serialized copy } @@ -898,7 +898,7 @@ mod legacy_misc_tests { false, ); // enable debugging and disable optimizations - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); let mut engine2 = Engine::default(); engine2.deserialize(&serialized).unwrap(); diff --git a/tests/live.rs b/tests/live.rs index 402fd89c..fc75f278 100644 --- a/tests/live.rs +++ b/tests/live.rs @@ -282,11 +282,11 @@ fn check_live_redirects() { /// deserializing from it. fn stable_serialization_through_load() { let engine1 = Engine::from_filter_set(ALL_FILTERS.lock().unwrap().clone(), true); - let ser1 = engine1.serialize().unwrap(); + let ser1 = engine1.serialize().to_vec(); let mut engine2 = Engine::default(); engine2.deserialize(&ser1).unwrap(); - let ser2 = engine2.serialize().unwrap(); + let ser2 = engine2.serialize().to_vec(); assert_eq!(ser1, ser2); } diff --git a/tests/ublock-coverage.rs b/tests/ublock-coverage.rs index 2c3b406a..94aa7aca 100644 --- a/tests/ublock-coverage.rs +++ b/tests/ublock-coverage.rs @@ -174,7 +174,7 @@ fn check_specifics_default() { #[test] fn check_basic_works_after_deserialization() { let engine = get_blocker_engine(); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); let mut deserialized_engine = Engine::default(); deserialized_engine.deserialize(&serialized).unwrap(); diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index ae7b6cea..1adefb94 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -153,10 +153,10 @@ mod tests { let mut engine = Engine::from_rules(filters, Default::default()); engine.enable_tags(&["stuff"]); engine.enable_tags(&["brian"]); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize(); let mut deserialized_engine = Engine::default(); deserialized_engine.enable_tags(&["stuff"]); - deserialized_engine.deserialize(&serialized).unwrap(); + deserialized_engine.deserialize(serialized).unwrap(); url_results.into_iter().for_each(|(url, expected_result)| { let request = Request::new(url, "", "").unwrap(); @@ -182,8 +182,8 @@ mod tests { #[test] fn deserialization_generate_simple() { let mut engine = Engine::from_rules(["ad-banner"], Default::default()); - let data = engine.serialize().unwrap(); - const EXPECTED_HASH: u64 = 14059407383857257100; + let data = engine.serialize().to_vec(); + const EXPECTED_HASH: u64 = 10824009878088122438; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -192,8 +192,8 @@ mod tests { fn deserialization_generate_tags() { let mut engine = Engine::from_rules(["ad-banner$tag=abc"], Default::default()); engine.use_tags(&["abc"]); - let data = engine.serialize().unwrap(); - const EXPECTED_HASH: u64 = 1772924818985173219; + let data = engine.serialize().to_vec(); + const EXPECTED_HASH: u64 = 14013192499527032437; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -207,7 +207,7 @@ mod tests { Resource::simple("noopcss", MimeType::TextCss, ""), ]); - let serialized = engine.serialize().unwrap(); + let serialized = engine.serialize().to_vec(); println!("Engine serialized: {:?}", serialized); engine.deserialize(&serialized).unwrap(); } @@ -216,12 +216,12 @@ mod tests { fn deserialization_brave_list() { let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]); let mut engine = Engine::from_rules_parametrised(rules, Default::default(), false, true); - let data = engine.serialize().unwrap(); + let data = engine.serialize().to_vec(); - let expected_hash = if cfg!(feature = "css-validation") { - 12046041060659687422 + let expected_hash: u64 = if cfg!(feature = "css-validation") { + 3923678787712771699 } else { - 11420623023091203502 + 12004038320442607485 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG); From c2bf91e9227757d6b3d208503992cd7cde4028d8 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 18 Aug 2025 20:46:06 +0700 Subject: [PATCH 02/21] use last byte to encode permissions --- src/cosmetic_filter_cache.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 0498f913..0384cc59 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -32,26 +32,24 @@ use flatbuffers::WIPOffset; use memchr::memchr as find_char; use serde::{Deserialize, Serialize}; -/// Encodes permission bits in the first byte of a script string +/// Encodes permission bits in the last byte of a script string /// Returns the script with permission byte prepended -fn encode_script_with_permission(script: String, permission: PermissionMask) -> String { - let mut encoded = String::with_capacity(script.len() + 1); - encoded.push(permission.to_bits() as char); - encoded.push_str(&script); - encoded +fn encode_script_with_permission(mut script: String, permission: PermissionMask) -> String { + script.push(permission.to_bits() as char); + script } -/// Decodes permission bits from the first byte of a script string +/// Decodes permission bits from the last byte of a script string /// Returns (permission, script) tuple fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { if encoded_script.is_empty() { return (PermissionMask::default(), encoded_script); } - let first_char = encoded_script.chars().next().unwrap(); - let permission_bits = first_char as u8; + let last_char = encoded_script.chars().last().unwrap(); + let permission_bits = last_char as u8; let permission = PermissionMask::from_bits(permission_bits); - let script = &encoded_script[first_char.len_utf8()..]; + let script = &encoded_script[..encoded_script.len() - 1]; (permission, script) } From c9d417cceacf97751ccf759dfe6acb154cc3dfaa Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 18 Aug 2025 20:46:46 +0700 Subject: [PATCH 03/21] unstage flat_map --- src/flatbuffers/containers/flat_map.rs | 69 -------------------------- 1 file changed, 69 deletions(-) delete mode 100644 src/flatbuffers/containers/flat_map.rs diff --git a/src/flatbuffers/containers/flat_map.rs b/src/flatbuffers/containers/flat_map.rs deleted file mode 100644 index 509eeb2e..00000000 --- a/src/flatbuffers/containers/flat_map.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::marker::PhantomData; - -use crate::flatbuffers::containers; -use containers::flat_serialize::{FlatBuilder, FlatMapBuilderOutput, FlatSerialize}; -use containers::sorted_index::SortedIndex; -use flatbuffers::{Follow, Vector}; - -pub(crate) struct FlatMapView<'a, I: Ord, V, Keys> -where - Keys: SortedIndex, - V: Follow<'a>, -{ - keys: Keys, - values: Vector<'a, V>, - _phantom: PhantomData, -} - -impl<'a, I: Ord + Copy, V, Keys> FlatMapView<'a, I, V, Keys> -where - Keys: SortedIndex + Clone, - V: flatbuffers::Follow<'a>, -{ - pub fn new(keys: Keys, values: Vector<'a, V>) -> Self { - debug_assert!(keys.len() == values.len()); - Self { - keys, - values, - _phantom: PhantomData, - } - } - - pub fn get(&self, key: I) -> Option<>::Inner> { - let index = self.keys.partition_point(|x| *x < key); - if index < self.keys.len() && self.keys.get(index) == key { - Some(self.values.get(index)) - } else { - None - } - } -} - -pub(crate) struct FlatMapBuilder; - -impl FlatMapBuilder { - pub fn finish<'a, I, V, B: FlatBuilder<'a>>( - value: std::collections::HashMap, - builder: &mut B, - ) -> FlatMapBuilderOutput<'a, I, V, B> - where - I: FlatSerialize<'a, B> + Ord, - V: FlatSerialize<'a, B>, - { - let mut entries: Vec<_> = value.into_iter().collect(); - entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); - - let mut indexes = Vec::with_capacity(entries.len()); - let mut values = Vec::with_capacity(entries.len()); - - for (key, value) in entries.into_iter() { - indexes.push(FlatSerialize::serialize(key, builder)); - values.push(FlatSerialize::serialize(value, builder)); - } - - FlatMapBuilderOutput { - keys: builder.raw_builder().create_vector(&indexes), - values: builder.raw_builder().create_vector(&values), - } - } -} From 50edf8201d0d53271c3ddcb0df6bc1c31145bfe7 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 25 Aug 2025 19:45:47 +0700 Subject: [PATCH 04/21] return flat map builder --- src/cosmetic_filter_cache.rs | 8 +-- src/flatbuffers/containers/flat_map.rs | 69 ++++++++++++++++++++++++++ tests/unit/engine.rs | 4 +- 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 src/flatbuffers/containers/flat_map.rs diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 0384cc59..e6e327b9 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -265,19 +265,19 @@ impl CosmeticFilterCacheBuilder { } // Handle remaining types through HostnameRule Unhide(s) => { - let entry = self.specific_rules.entry(*token).or_default(); + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); entry.unhide.push(s); } UninjectScript((s, _)) => { - let entry = self.specific_rules.entry(*token).or_default(); + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); entry.uninject_script.push(s); } ProceduralOrAction(s) => { - let entry = self.specific_rules.entry(*token).or_default(); + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); entry.procedural_action.push(s); } ProceduralOrActionException(s) => { - let entry = self.specific_rules.entry(*token).or_default(); + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); entry.procedural_action_exception.push(s); } } diff --git a/src/flatbuffers/containers/flat_map.rs b/src/flatbuffers/containers/flat_map.rs new file mode 100644 index 00000000..509eeb2e --- /dev/null +++ b/src/flatbuffers/containers/flat_map.rs @@ -0,0 +1,69 @@ +use std::marker::PhantomData; + +use crate::flatbuffers::containers; +use containers::flat_serialize::{FlatBuilder, FlatMapBuilderOutput, FlatSerialize}; +use containers::sorted_index::SortedIndex; +use flatbuffers::{Follow, Vector}; + +pub(crate) struct FlatMapView<'a, I: Ord, V, Keys> +where + Keys: SortedIndex, + V: Follow<'a>, +{ + keys: Keys, + values: Vector<'a, V>, + _phantom: PhantomData, +} + +impl<'a, I: Ord + Copy, V, Keys> FlatMapView<'a, I, V, Keys> +where + Keys: SortedIndex + Clone, + V: flatbuffers::Follow<'a>, +{ + pub fn new(keys: Keys, values: Vector<'a, V>) -> Self { + debug_assert!(keys.len() == values.len()); + Self { + keys, + values, + _phantom: PhantomData, + } + } + + pub fn get(&self, key: I) -> Option<>::Inner> { + let index = self.keys.partition_point(|x| *x < key); + if index < self.keys.len() && self.keys.get(index) == key { + Some(self.values.get(index)) + } else { + None + } + } +} + +pub(crate) struct FlatMapBuilder; + +impl FlatMapBuilder { + pub fn finish<'a, I, V, B: FlatBuilder<'a>>( + value: std::collections::HashMap, + builder: &mut B, + ) -> FlatMapBuilderOutput<'a, I, V, B> + where + I: FlatSerialize<'a, B> + Ord, + V: FlatSerialize<'a, B>, + { + let mut entries: Vec<_> = value.into_iter().collect(); + entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); + + let mut indexes = Vec::with_capacity(entries.len()); + let mut values = Vec::with_capacity(entries.len()); + + for (key, value) in entries.into_iter() { + indexes.push(FlatSerialize::serialize(key, builder)); + values.push(FlatSerialize::serialize(value, builder)); + } + + FlatMapBuilderOutput { + keys: builder.raw_builder().create_vector(&indexes), + values: builder.raw_builder().create_vector(&values), + } + } +} diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 1adefb94..a2560d09 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -219,9 +219,9 @@ mod tests { let data = engine.serialize().to_vec(); let expected_hash: u64 = if cfg!(feature = "css-validation") { - 3923678787712771699 + 8016301231738552031 } else { - 12004038320442607485 + 8518366622637220045 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG); From 16013a40becff01eada873b08b83ef284f1a2dca Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 25 Aug 2025 20:21:40 +0700 Subject: [PATCH 05/21] decompose network filters --- src/filters/fb_builder.rs | 293 +----------------------------- src/filters/fb_network.rs | 2 +- src/filters/fb_network_builder.rs | 291 +++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 286 deletions(-) create mode 100644 src/filters/fb_network_builder.rs diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 5b83ccae..3850726d 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -1,45 +1,23 @@ //! Builder for creating flatbuffer with serialized engine. -//! Currently the work in progress, therefore only some fields of Engine -//! are serialized to flatbuffer. -//! The entry point is `FlatBufferBuilder::make_flatbuffer`. +//! The entry point is `make_flatbuffer`. -use std::collections::{HashMap, HashSet}; -use std::vec; +use std::collections::HashMap; use flatbuffers::WIPOffset; -use crate::cosmetic_filter_cache::CosmeticFilterCacheBuilder; +use crate::cosmetic_filter_cache_builder::CosmeticFilterCacheBuilder; use crate::filters::cosmetic::CosmeticFilter; -use crate::filters::network::{NetworkFilter, NetworkFilterMaskHelper}; -use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; +use crate::filters::fb_network_builder::{NetworkFilterListBuilder, NetworkRulesBuilder}; +use crate::filters::network::NetworkFilter; use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, FlatSerialize, WIPFlatVec}; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; -use crate::network_filter_list::token_histogram; -use crate::optimizer; -use crate::utils::{to_short_hash, Hash, ShortHash}; +use crate::utils::Hash; use super::fb_network::flat::fb; -pub(crate) enum NetworkFilterListId { - Csp = 0, - Exceptions = 1, - Importants = 2, - Redirects = 3, - RemoveParam = 4, - Filters = 5, - GenericHide = 6, - TaggedFiltersAll = 7, - Size = 8, -} - -#[derive(Default, Clone)] -struct NetworkFilterListBuilder { - filters: Vec, - optimize: bool, -} #[derive(Default)] -struct EngineFlatBuilder<'a> { +pub(crate) struct EngineFlatBuilder<'a> { fb_builder: flatbuffers::FlatBufferBuilder<'a>, unique_domains_hashes: Vec, unique_domains_hashes_map: HashMap, @@ -88,263 +66,8 @@ impl<'a> FlatBuilder<'a> for EngineFlatBuilder<'a> { } } -struct NetworkRulesBuilder { - lists: Vec, -} - -impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { - type Output = WIPOffset>; - - fn serialize( - network_filter: &NetworkFilter, - builder: &mut EngineFlatBuilder<'a>, - ) -> WIPOffset> { - let opt_domains = network_filter.opt_domains.as_ref().map(|v| { - let mut o: Vec = v - .iter() - .map(|x| builder.get_or_insert_unique_domain_hash(x)) - .collect(); - o.sort_unstable(); - o.dedup(); - FlatSerialize::serialize(o, builder) - }); - - let opt_not_domains = network_filter.opt_not_domains.as_ref().map(|v| { - let mut o: Vec = v - .iter() - .map(|x| builder.get_or_insert_unique_domain_hash(x)) - .collect(); - o.sort_unstable(); - o.dedup(); - FlatSerialize::serialize(o, builder) - }); - - let modifier_option = network_filter - .modifier_option - .as_ref() - .map(|s| builder.create_string(s)); - - let hostname = network_filter - .hostname - .as_ref() - .map(|s| builder.create_string(s)); - - let tag = network_filter - .tag - .as_ref() - .map(|s| builder.create_string(s)); - - let patterns = if network_filter.filter.iter().len() > 0 { - let offsets: Vec> = network_filter - .filter - .iter() - .map(|s| builder.create_string(s)) - .collect(); - Some(FlatSerialize::serialize(offsets, builder)) - } else { - None - }; - - let raw_line = network_filter - .raw_line - .as_ref() - .map(|v| builder.create_string(v.as_str())); - - let network_filter = fb::NetworkFilter::create( - &mut builder.fb_builder, - &fb::NetworkFilterArgs { - mask: network_filter.mask.bits(), - patterns, - modifier_option, - opt_domains, - opt_not_domains, - hostname, - tag, - raw_line, - }, - ); - - network_filter - } -} - -impl NetworkFilterListBuilder { - fn new(optimize: bool) -> Self { - Self { - filters: vec![], - optimize, - } - } -} - -impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkFilterListBuilder { - type Output = WIPOffset>; - fn serialize( - rule_list: Self, - builder: &mut EngineFlatBuilder<'a>, - ) -> WIPOffset> { - let mut filter_map = HashMap::>>>::new(); - - let mut optimizable = HashMap::>::new(); - - // Compute tokens for all filters - let filter_tokens: Vec<_> = rule_list - .filters - .into_iter() - .map(|filter| { - let tokens = filter.get_tokens(); - (filter, tokens) - }) - .collect(); - - // compute the tokens' frequency histogram - let (total_number_of_tokens, tokens_histogram) = token_histogram(&filter_tokens); - - { - for (network_filter, multi_tokens) in filter_tokens.into_iter() { - let flat_filter = if !rule_list.optimize - || !optimizer::is_filter_optimizable_by_patterns(&network_filter) - { - Some(FlatSerialize::serialize(&network_filter, builder)) - } else { - None - }; - - for tokens in multi_tokens { - let mut best_token: ShortHash = 0; - let mut min_count = total_number_of_tokens + 1; - for token in tokens { - let token = to_short_hash(token); - match tokens_histogram.get(&token) { - None => { - min_count = 0; - best_token = token - } - Some(&count) if count < min_count => { - min_count = count; - best_token = token - } - _ => {} - } - } - - if let Some(flat_filter) = flat_filter { - filter_map.entry(best_token).or_default().push(flat_filter); - } else { - optimizable - .entry(best_token) - .or_default() - .push(network_filter.clone()); - } - } // tokens - } - } - - if rule_list.optimize { - // Sort the entries to ensure deterministic iteration order - let mut optimizable_entries: Vec<_> = optimizable.drain().collect(); - optimizable_entries.sort_unstable_by_key(|(token, _)| *token); - - for (token, v) in optimizable_entries { - let optimized = optimizer::optimize(v); - - for filter in optimized { - let flat_filter = FlatSerialize::serialize(&filter, builder); - filter_map.entry(token).or_default().push(flat_filter); - } - } - } else { - debug_assert!( - optimizable.is_empty(), - "Should be empty if optimization is off" - ); - } - - let flat_filter_map_builder = FlatMultiMapBuilder::from_filter_map(filter_map); - let flat_filter_map = FlatMultiMapBuilder::finish(flat_filter_map_builder, builder); - - fb::NetworkFilterList::create( - builder.raw_builder(), - &fb::NetworkFilterListArgs { - filter_map_index: Some(flat_filter_map.keys), - filter_map_values: Some(flat_filter_map.values), - }, - ) - } -} - -impl NetworkRulesBuilder { - pub fn from_rules(network_filters: Vec, optimize: bool) -> Self { - let mut lists = vec![]; - for list_id in 0..NetworkFilterListId::Size as usize { - // Don't optimize removeparam, since it can fuse filters without respecting distinct - let optimize = optimize && list_id != NetworkFilterListId::RemoveParam as usize; - lists.push(NetworkFilterListBuilder::new(optimize)); - } - let mut self_ = Self { lists }; - - let mut badfilter_ids: HashSet = HashSet::new(); - - // Collect badfilter ids in advance. - for filter in network_filters.iter() { - if filter.is_badfilter() { - badfilter_ids.insert(filter.get_id_without_badfilter()); - } - } - - for filter in network_filters.into_iter() { - // skip any bad filters - let filter_id = filter.get_id(); - if badfilter_ids.contains(&filter_id) || filter.is_badfilter() { - continue; - } - - // Redirects are independent of blocking behavior. - if filter.is_redirect() { - self_.add_filter(filter.clone(), NetworkFilterListId::Redirects); - } - type FilterId = NetworkFilterListId; - - let list_id: FilterId = if filter.is_csp() { - FilterId::Csp - } else if filter.is_removeparam() { - FilterId::RemoveParam - } else if filter.is_generic_hide() { - FilterId::GenericHide - } else if filter.is_exception() { - FilterId::Exceptions - } else if filter.is_important() { - FilterId::Importants - } else if filter.tag.is_some() && !filter.is_redirect() { - // `tag` + `redirect` is unsupported for now. - FilterId::TaggedFiltersAll - } else if (filter.is_redirect() && filter.also_block_redirect()) - || !filter.is_redirect() - { - FilterId::Filters - } else { - continue; - }; - - self_.add_filter(filter, list_id); - } - - self_ - } - - fn add_filter(&mut self, network_filter: NetworkFilter, list_id: NetworkFilterListId) { - self.lists[list_id as usize].filters.push(network_filter); - } -} - -impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder { - type Output = WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>; - fn serialize(value: Self, builder: &mut EngineFlatBuilder<'a>) -> Self::Output { - FlatSerialize::serialize(value.lists, builder) - } -} -pub fn make_flatbuffer_from_rules( +pub fn make_flatbuffer( network_filters: Vec, cosmetic_filters: Vec, optimize: bool, diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index 93abb1a3..43a44d49 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -90,7 +90,7 @@ pub(crate) struct FilterDataContext { impl Default for FilterDataContext { fn default() -> Self { Self { - memory: crate::filters::fb_builder::make_flatbuffer_from_rules( + memory: crate::filters::fb_builder::make_flatbuffer( vec![], vec![], false, diff --git a/src/filters/fb_network_builder.rs b/src/filters/fb_network_builder.rs new file mode 100644 index 00000000..e37d5550 --- /dev/null +++ b/src/filters/fb_network_builder.rs @@ -0,0 +1,291 @@ +//! Structures to store network filters to flatbuffer + +use std::collections::{HashMap, HashSet}; + +use flatbuffers::WIPOffset; + +use crate::filters::fb_builder::EngineFlatBuilder; +use crate::filters::network::NetworkFilter; + +use crate::filters::network::{NetworkFilterMaskHelper}; +use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; +use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, FlatSerialize, WIPFlatVec}; +use crate::network_filter_list::token_histogram; +use crate::optimizer; +use crate::utils::{to_short_hash, Hash, ShortHash}; + +use super::fb_network::flat::fb; + +pub(crate) enum NetworkFilterListId { + Csp = 0, + Exceptions = 1, + Importants = 2, + Redirects = 3, + RemoveParam = 4, + Filters = 5, + GenericHide = 6, + TaggedFiltersAll = 7, + Size = 8, +} + +#[derive(Default, Clone)] +pub(crate) struct NetworkFilterListBuilder { + filters: Vec, + optimize: bool, +} + +pub(crate) struct NetworkRulesBuilder { + lists: Vec, +} + +impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { + type Output = WIPOffset>; + + fn serialize( + network_filter: &NetworkFilter, + builder: &mut EngineFlatBuilder<'a>, + ) -> WIPOffset> { + let opt_domains = network_filter.opt_domains.as_ref().map(|v| { + let mut o: Vec = v + .iter() + .map(|x| builder.get_or_insert_unique_domain_hash(x)) + .collect(); + o.sort_unstable(); + o.dedup(); + FlatSerialize::serialize(o, builder) + }); + + let opt_not_domains = network_filter.opt_not_domains.as_ref().map(|v| { + let mut o: Vec = v + .iter() + .map(|x| builder.get_or_insert_unique_domain_hash(x)) + .collect(); + o.sort_unstable(); + o.dedup(); + FlatSerialize::serialize(o, builder) + }); + + let modifier_option = network_filter + .modifier_option + .as_ref() + .map(|s| builder.create_string(s)); + + let hostname = network_filter + .hostname + .as_ref() + .map(|s| builder.create_string(s)); + + let tag = network_filter + .tag + .as_ref() + .map(|s| builder.create_string(s)); + + let patterns = if network_filter.filter.iter().len() > 0 { + let offsets: Vec> = network_filter + .filter + .iter() + .map(|s| builder.create_string(s)) + .collect(); + Some(FlatSerialize::serialize(offsets, builder)) + } else { + None + }; + + let raw_line = network_filter + .raw_line + .as_ref() + .map(|v| builder.create_string(v.as_str())); + + let network_filter = fb::NetworkFilter::create( + &mut builder.raw_builder(), + &fb::NetworkFilterArgs { + mask: network_filter.mask.bits(), + patterns, + modifier_option, + opt_domains, + opt_not_domains, + hostname, + tag, + raw_line, + }, + ); + + network_filter + } +} + +impl NetworkFilterListBuilder { + fn new(optimize: bool) -> Self { + Self { + filters: vec![], + optimize, + } + } +} + +impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkFilterListBuilder { + type Output = WIPOffset>; + fn serialize( + rule_list: Self, + builder: &mut EngineFlatBuilder<'a>, + ) -> WIPOffset> { + let mut filter_map = HashMap::>>>::new(); + + let mut optimizable = HashMap::>::new(); + + // Compute tokens for all filters + let filter_tokens: Vec<_> = rule_list + .filters + .into_iter() + .map(|filter| { + let tokens = filter.get_tokens(); + (filter, tokens) + }) + .collect(); + + // compute the tokens' frequency histogram + let (total_number_of_tokens, tokens_histogram) = token_histogram(&filter_tokens); + + { + for (network_filter, multi_tokens) in filter_tokens.into_iter() { + let flat_filter = if !rule_list.optimize + || !optimizer::is_filter_optimizable_by_patterns(&network_filter) + { + Some(FlatSerialize::serialize(&network_filter, builder)) + } else { + None + }; + + for tokens in multi_tokens { + let mut best_token: ShortHash = 0; + let mut min_count = total_number_of_tokens + 1; + for token in tokens { + let token = to_short_hash(token); + match tokens_histogram.get(&token) { + None => { + min_count = 0; + best_token = token + } + Some(&count) if count < min_count => { + min_count = count; + best_token = token + } + _ => {} + } + } + + if let Some(flat_filter) = flat_filter { + filter_map.entry(best_token).or_default().push(flat_filter); + } else { + optimizable + .entry(best_token) + .or_default() + .push(network_filter.clone()); + } + } // tokens + } + } + + if rule_list.optimize { + // Sort the entries to ensure deterministic iteration order + let mut optimizable_entries: Vec<_> = optimizable.drain().collect(); + optimizable_entries.sort_unstable_by_key(|(token, _)| *token); + + for (token, v) in optimizable_entries { + let optimized = optimizer::optimize(v); + + for filter in optimized { + let flat_filter = FlatSerialize::serialize(&filter, builder); + filter_map.entry(token).or_default().push(flat_filter); + } + } + } else { + debug_assert!( + optimizable.is_empty(), + "Should be empty if optimization is off" + ); + } + + let flat_filter_map_builder = FlatMultiMapBuilder::from_filter_map(filter_map); + let flat_filter_map = FlatMultiMapBuilder::finish(flat_filter_map_builder, builder); + + fb::NetworkFilterList::create( + builder.raw_builder(), + &fb::NetworkFilterListArgs { + filter_map_index: Some(flat_filter_map.keys), + filter_map_values: Some(flat_filter_map.values), + }, + ) + } +} + +impl NetworkRulesBuilder { + pub fn from_rules(network_filters: Vec, optimize: bool) -> Self { + let mut lists = vec![]; + for list_id in 0..NetworkFilterListId::Size as usize { + // Don't optimize removeparam, since it can fuse filters without respecting distinct + let optimize = optimize && list_id != NetworkFilterListId::RemoveParam as usize; + lists.push(NetworkFilterListBuilder::new(optimize)); + } + let mut self_ = Self { lists }; + + let mut badfilter_ids: HashSet = HashSet::new(); + + // Collect badfilter ids in advance. + for filter in network_filters.iter() { + if filter.is_badfilter() { + badfilter_ids.insert(filter.get_id_without_badfilter()); + } + } + + for filter in network_filters.into_iter() { + // skip any bad filters + let filter_id = filter.get_id(); + if badfilter_ids.contains(&filter_id) || filter.is_badfilter() { + continue; + } + + // Redirects are independent of blocking behavior. + if filter.is_redirect() { + self_.add_filter(filter.clone(), NetworkFilterListId::Redirects); + } + type FilterId = NetworkFilterListId; + + let list_id: FilterId = if filter.is_csp() { + FilterId::Csp + } else if filter.is_removeparam() { + FilterId::RemoveParam + } else if filter.is_generic_hide() { + FilterId::GenericHide + } else if filter.is_exception() { + FilterId::Exceptions + } else if filter.is_important() { + FilterId::Importants + } else if filter.tag.is_some() && !filter.is_redirect() { + // `tag` + `redirect` is unsupported for now. + FilterId::TaggedFiltersAll + } else if (filter.is_redirect() && filter.also_block_redirect()) + || !filter.is_redirect() + { + FilterId::Filters + } else { + continue; + }; + + self_.add_filter(filter, list_id); + } + + self_ + } + + fn add_filter(&mut self, network_filter: NetworkFilter, list_id: NetworkFilterListId) { + self.lists[list_id as usize].filters.push(network_filter); + } +} + +impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder { + type Output = WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>; + fn serialize(value: Self, builder: &mut EngineFlatBuilder<'a>) -> Self::Output { + FlatSerialize::serialize(value.lists, builder) + } +} From d4dd1881d3cebf4d3636406796b9ce4c35bc3590 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 25 Aug 2025 20:26:57 +0700 Subject: [PATCH 06/21] decompose cosmetic filters --- src/blocker.rs | 6 +- src/cosmetic_filter_cache.rs | 792 ++++++++------------------- src/cosmetic_filter_cache_builder.rs | 247 +++++++++ src/cosmetic_filter_utils.rs | 104 ++++ src/engine.rs | 4 +- src/filters/mod.rs | 1 + src/lib.rs | 2 + tests/unit/cosmetic_filter_cache.rs | 2 +- 8 files changed, 592 insertions(+), 566 deletions(-) create mode 100644 src/cosmetic_filter_cache_builder.rs create mode 100644 src/cosmetic_filter_utils.rs diff --git a/src/blocker.rs b/src/blocker.rs index 4952a477..96424004 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -6,7 +6,7 @@ use serde::Serialize; use std::collections::HashSet; use std::ops::DerefMut; -use crate::filters::fb_builder::NetworkFilterListId; +use crate::filters::fb_network_builder::NetworkFilterListId; use crate::filters::fb_network::FilterDataContextRef; use crate::filters::network::NetworkFilterMaskHelper; use crate::network_filter_list::NetworkFilterList; @@ -440,9 +440,9 @@ impl Blocker { network_filters: Vec, options: &BlockerOptions, ) -> Self { - use crate::filters::{fb_builder::make_flatbuffer_from_rules, fb_network::FilterDataContext}; + use crate::filters::{fb_builder::make_flatbuffer, fb_network::FilterDataContext}; - let memory = make_flatbuffer_from_rules( + let memory = make_flatbuffer( network_filters, vec![], // no cosmetic filters for blocker test options.enable_optimizations, diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index e6e327b9..53194fc9 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -8,51 +8,27 @@ //! cosmetic filters and allows them to be queried efficiently at runtime for any which may be //! relevant to a particular page. +use crate::cosmetic_filter_utils::decode_script_with_permission; +#[cfg(test)] +use crate::filters::cosmetic::CosmeticFilter; use crate::filters::cosmetic::{ - CosmeticFilter, CosmeticFilterAction, CosmeticFilterMask, CosmeticFilterOperator, + CosmeticFilterAction, CosmeticFilterOperator, }; -use crate::filters::fb_network::flat::fb; use crate::filters::fb_network::FilterDataContextRef; -use crate::flatbuffers::containers::flat_map::{FlatMapBuilder, FlatMapView}; -use crate::flatbuffers::containers::flat_multimap::{ - FlatMapStringView, FlatMultiMapBuilder, FlatMultiMapView, -}; -use crate::flatbuffers::containers::flat_serialize::{ - serialize_vec_opt, FlatBuilder, FlatSerialize, +use crate::flatbuffers::containers::flat_map::FlatMapView; +use crate::flatbuffers::containers::flat_multimap::{ + FlatMapStringView, FlatMultiMapView, }; use crate::flatbuffers::containers::flat_set::FlatSetView; - use crate::resources::{PermissionMask, ResourceStorage}; + use crate::utils::Hash; use std::collections::{HashMap, HashSet}; -use flatbuffers::WIPOffset; -use memchr::memchr as find_char; use serde::{Deserialize, Serialize}; -/// Encodes permission bits in the last byte of a script string -/// Returns the script with permission byte prepended -fn encode_script_with_permission(mut script: String, permission: PermissionMask) -> String { - script.push(permission.to_bits() as char); - script -} - -/// Decodes permission bits from the last byte of a script string -/// Returns (permission, script) tuple -fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { - if encoded_script.is_empty() { - return (PermissionMask::default(), encoded_script); - } - - let last_char = encoded_script.chars().last().unwrap(); - let permission_bits = last_char as u8; - let permission = PermissionMask::from_bits(permission_bits); - let script = &encoded_script[..encoded_script.len() - 1]; - (permission, script) -} - /// Contains cosmetic filter information intended to be used on a particular URL. #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct UrlSpecificResources { @@ -100,457 +76,6 @@ pub(crate) struct CosmeticFilterCache { filter_data_context: FilterDataContextRef, } -/// Accumulates hostname-specific rules for a single domain before building HostnameSpecificRules -/// Note: hide and inject_script are now handled separately at the top level -#[derive(Default)] -struct HostnameRule { - unhide: Vec, - uninject_script: Vec, - procedural_action: Vec, - procedural_action_exception: Vec, -} - -impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for HostnameRule { - type Output = WIPOffset>; - - fn serialize( - value: Self, - builder: &mut B, - ) -> flatbuffers::WIPOffset> { - let unhide = serialize_vec_opt(value.unhide, builder); - let uninject_script = serialize_vec_opt(value.uninject_script, builder); - let procedural_action = serialize_vec_opt(value.procedural_action, builder); - let procedural_action_exception = - serialize_vec_opt(value.procedural_action_exception, builder); - - fb::HostnameSpecificRules::create( - builder.raw_builder(), - &fb::HostnameSpecificRulesArgs { - unhide, - uninject_script, - procedural_action, - procedural_action_exception, - }, - ) - } -} - -#[derive(Default)] -pub(crate) struct CosmeticFilterCacheBuilder { - simple_class_rules: HashSet, - simple_id_rules: HashSet, - misc_generic_selectors: HashSet, - complex_class_rules: FlatMultiMapBuilder, - complex_id_rules: FlatMultiMapBuilder, - - hostname_hide: FlatMultiMapBuilder, - hostname_inject_script: FlatMultiMapBuilder, - - specific_rules: HashMap, -} - -impl CosmeticFilterCacheBuilder { - pub fn from_rules(rules: Vec) -> Self { - let mut self_ = Self::default(); - - for rule in rules { - self_.add_filter(rule) - } - - self_ - } - - pub fn add_filter(&mut self, rule: CosmeticFilter) { - if rule.has_hostname_constraint() { - if let Some(generic_rule) = rule.hidden_generic_rule() { - self.add_generic_filter(generic_rule); - } - self.store_hostname_rule(rule); - } else { - self.add_generic_filter(rule); - } - } - - /// Add a filter, assuming it has already been determined to be a generic rule - fn add_generic_filter(&mut self, rule: CosmeticFilter) { - let selector = match rule.plain_css_selector() { - Some(s) => s.to_string(), - None => { - // Procedural cosmetic filters cannot be generic. - // Silently ignoring this filter. - return; - } - }; - - if selector.starts_with('.') { - if let Some(key) = key_from_selector(&selector) { - assert!(key.starts_with('.')); - let class = key[1..].to_string(); - if key == selector { - self.simple_class_rules.insert(class); - } else { - self.complex_class_rules.insert(class, selector); - } - } - } else if selector.starts_with('#') { - if let Some(key) = key_from_selector(&selector) { - assert!(key.starts_with('#')); - let id = key[1..].to_string(); - if key == selector { - self.simple_id_rules.insert(id); - } else { - self.complex_id_rules.insert(id, selector); - } - } - } else { - self.misc_generic_selectors.insert(selector); - } - } - - // TODO: review this - fn store_hostname_rule(&mut self, rule: CosmeticFilter) { - use SpecificFilterType::*; - - let unhide = rule.mask.contains(CosmeticFilterMask::UNHIDE); - let script_inject = rule.mask.contains(CosmeticFilterMask::SCRIPT_INJECT); - - let kind = match ( - script_inject, - rule.plain_css_selector().map(|s| s.to_string()), - rule.action, - ) { - (false, Some(selector), None) => Hide(selector), - (true, Some(selector), None) => InjectScript((selector, rule.permission)), - (false, selector, action) => ProceduralOrAction( - serde_json::to_string(&ProceduralOrActionFilter { - selector: selector - .map(|selector| vec![CosmeticFilterOperator::CssSelector(selector)]) - .unwrap_or(rule.selector), - action, - }) - .unwrap(), - ), - (true, _, Some(_)) => return, // script injection with action - shouldn't be possible - (true, None, _) => return, // script injection without plain CSS selector - shouldn't be possible - }; - - let kind = if unhide { kind.negated() } else { kind }; - - let tokens_to_insert = std::iter::empty() - .chain(rule.hostnames.unwrap_or_default()) - .chain(rule.entities.unwrap_or_default()); - - tokens_to_insert.for_each(|t| self.store_hostname_filter(&t, kind.clone())); - - let tokens_to_insert_negated = std::iter::empty() - .chain(rule.not_hostnames.unwrap_or_default()) - .chain(rule.not_entities.unwrap_or_default()); - - let negated = kind.negated(); - - tokens_to_insert_negated.for_each(|t| self.store_hostname_filter(&t, negated.clone())); - } - - fn store_hostname_filter(&mut self, token: &Hash, kind: SpecificFilterType) { - use SpecificFilterType::*; - - match kind { - // Handle hide and inject_script at top level for better deduplication - Hide(s) => { - self.hostname_hide.insert(*token, s); - } - InjectScript((s, permission)) => { - let encoded_script = encode_script_with_permission(s, permission); - self.hostname_inject_script.insert(*token, encoded_script); - } - // Handle remaining types through HostnameRule - Unhide(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); - entry.unhide.push(s); - } - UninjectScript((s, _)) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); - entry.uninject_script.push(s); - } - ProceduralOrAction(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); - entry.procedural_action.push(s); - } - ProceduralOrActionException(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); - entry.procedural_action_exception.push(s); - } - } - } -} - -impl CosmeticFilterCache { - pub fn from_context(filter_data_context: FilterDataContextRef) -> Self { - Self { - filter_data_context, - } - } - - #[cfg(test)] - pub fn from_rules(rules: Vec) -> Self { - use crate::filters::{ - fb_builder::make_flatbuffer_from_rules, fb_network::FilterDataContext, - }; - - let memory = make_flatbuffer_from_rules(vec![], rules, true, 0); - - let filter_data_context = FilterDataContext::new(memory); - Self::from_context(filter_data_context) - } - - /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they - /// apply to all sites. Rather than injecting all of these rules onto every page, which would - /// blow up memory usage, we only inject rules based on classes and ids that actually appear on - /// the page (in practice, a `MutationObserver` is used to identify those elements). We can - /// include rules like `.a-class div#ads > .advertisement`, keyed by the `.a-class` selector, - /// since we know that this rule cannot possibly apply unless there is an `.a-class` element on - /// the page. - /// - /// This method returns all of the generic CSS selectors of elements to hide (i.e. with a - /// `display: none !important` CSS rule) that could possibly be or become relevant to the page - /// given the new classes and ids that have appeared on the page. It guarantees that it will be - /// safe to hide those elements on a particular page by taking into account the page's - /// hostname-specific set of exception rules. - /// - /// The exceptions should be returned directly as they appear in the page's - /// `UrlSpecificResources`. The exceptions, along with the set of already-seen classes and ids, - /// must be cached externally as the cosmetic filtering subsystem here is designed to be - /// stateless with regard to active page sessions. - pub fn hidden_class_id_selectors( - &self, - classes: impl IntoIterator>, - ids: impl IntoIterator>, - exceptions: &HashSet, - ) -> Vec { - let mut selectors = vec![]; - - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); - let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); - let complex_class_rules = FlatMapStringView::new( - cs.complex_class_rules_index(), - cs.complex_class_rules_values(), - ); - let complex_id_rules = - FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); - - classes.into_iter().for_each(|class| { - let class = class.as_ref(); - if simple_class_rules.contains(class) && !exceptions.contains(&format!(".{}", class)) { - selectors.push(format!(".{}", class)); - } - if let Some(bucket) = complex_class_rules.get(class) { - for (_, sel) in bucket { - if !exceptions.contains(sel) { - selectors.push(sel.to_string()); - } - } - } - }); - ids.into_iter().for_each(|id| { - let id = id.as_ref(); - if simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { - selectors.push(format!("#{}", id)); - } - if let Some(bucket) = complex_id_rules.get(id) { - for (_, sel) in bucket { - if !exceptions.contains(sel) { - selectors.push(sel.to_string()); - } - } - } - }); - - selectors - } - - /// Any rules that can't be handled by `hidden_class_id_selectors` are returned by - /// `hostname_cosmetic_resources`. As soon as a page navigation is committed, this method - /// should be queried to get the initial set of cosmetic filtering operations to apply to the - /// page. This provides any rules specifying elements to hide by selectors that are too complex - /// to be returned by `hidden_class_id_selectors` (i.e. not directly starting with a class or - /// id selector, like `div[class*="Ads"]`), or any rule that is only applicable to a particular - /// hostname or set of hostnames (like `example.com##.a-class`). The first category is always - /// injected into every page, and makes up a relatively small number of rules in practice. - pub fn hostname_cosmetic_resources( - &self, - resources: &ResourceStorage, - hostname: &str, - generichide: bool, - ) -> UrlSpecificResources { - let domain_str = { - let (start, end) = crate::url_parser::get_host_domain(hostname); - &hostname[start..end] - }; - - let (request_entities, request_hostnames) = hostname_domain_hashes(hostname, domain_str); - - let mut specific_hide_selectors = HashSet::new(); - let mut procedural_actions = HashSet::new(); - let mut script_injections = HashMap::<&str, PermissionMask>::new(); - let mut exceptions = HashSet::new(); - - let mut except_all_scripts = false; - - let hashes: Vec<&Hash> = request_entities - .iter() - .chain(request_hostnames.iter()) - .collect(); - - let cf = self.filter_data_context.memory.root().cosmetic_filters(); - let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); - let hostname_hide_view = - FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); - let hostname_inject_script_view = FlatMultiMapView::new( - cf.hostname_inject_script_index(), - cf.hostname_inject_script_values(), - ); - - for hash in hashes.iter() { - // Handle top-level hide selectors - if let Some(hide_iterator) = hostname_hide_view.get(**hash) { - for (_, hide_selector) in hide_iterator { - if !exceptions.contains(hide_selector) { - specific_hide_selectors.insert(hide_selector.to_owned()); - } - } - } - - // Handle top-level inject scripts with encoded permissions - if let Some(script_iterator) = hostname_inject_script_view.get(**hash) { - for (_, encoded_script) in script_iterator { - let (permission, script) = decode_script_with_permission(encoded_script); - script_injections - .entry(script) - .and_modify(|entry| *entry |= permission) - .or_insert(permission); - } - } - - // Handle remaining rule types from HostnameSpecificRules - if let Some(hostname_rules) = hostname_rules_view.get(**hash) { - // Process procedural actions - if let Some(procedural_actions_rules) = hostname_rules.procedural_action() { - for action in procedural_actions_rules.iter() { - procedural_actions.insert(action.to_owned()); - } - } - } - } - - // Process unhide/exception filters - for hash in hashes.iter() { - if let Some(hostname_rules) = hostname_rules_view.get(**hash) { - // Process unhide selectors (special behavior: they also go in exceptions) - if let Some(unhide_rules) = hostname_rules.unhide() { - for selector in unhide_rules.iter() { - specific_hide_selectors.remove(selector); - exceptions.insert(selector.to_owned()); - } - } - - // Process procedural action exceptions - if let Some(procedural_exceptions) = hostname_rules.procedural_action_exception() { - for action in procedural_exceptions.iter() { - procedural_actions.remove(action); - } - } - - // Process script uninjects - if let Some(uninject_scripts) = hostname_rules.uninject_script() { - for script in uninject_scripts.iter() { - if script.is_empty() { - except_all_scripts = true; - script_injections.clear(); - } - if except_all_scripts { - continue; - } - script_injections.remove(script); - } - } - } - } - - let hide_selectors = if generichide { - specific_hide_selectors - } else { - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let misc_generic_selectors_vector = cs.misc_generic_selectors(); - - // TODO: check performance of this - let mut hide_selectors = HashSet::new(); - for i in 0..misc_generic_selectors_vector.len() { - let selector = misc_generic_selectors_vector.get(i); - if !exceptions.contains(selector) { - hide_selectors.insert(selector.to_string()); - } - } - specific_hide_selectors.into_iter().for_each(|sel| { - hide_selectors.insert(sel); - }); - hide_selectors - }; - - let injected_script = resources.get_scriptlet_resources(script_injections); - - UrlSpecificResources { - hide_selectors, - procedural_actions, - exceptions, - injected_script, - generichide, - } - } -} - -impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for CosmeticFilterCacheBuilder { - type Output = WIPOffset>; - fn serialize(value: Self, builder: &mut B) -> WIPOffset> { - let complex_class_rules = FlatMultiMapBuilder::finish(value.complex_class_rules, builder); - let complex_id_rules = FlatMultiMapBuilder::finish(value.complex_id_rules, builder); - - // Handle top-level hostname hide and inject_script for better deduplication - let hostname_hide = FlatMultiMapBuilder::finish(value.hostname_hide, builder); - let hostname_inject_script = - FlatMultiMapBuilder::finish(value.hostname_inject_script, builder); - - // Handle remaining rule types through HostnameSpecificRules - let hostname_specific_rules = FlatMapBuilder::finish(value.specific_rules, builder); - - let simple_class_rules = Some(FlatSerialize::serialize(value.simple_class_rules, builder)); - let simple_id_rules = Some(FlatSerialize::serialize(value.simple_id_rules, builder)); - let misc_generic_selectors = Some(FlatSerialize::serialize( - value.misc_generic_selectors, - builder, - )); - - fb::CosmeticFilters::create( - builder.raw_builder(), - &fb::CosmeticFiltersArgs { - simple_class_rules, - simple_id_rules, - misc_generic_selectors, - complex_class_rules_index: Some(complex_class_rules.keys), - complex_class_rules_values: Some(complex_class_rules.values), - complex_id_rules_index: Some(complex_id_rules.keys), - complex_id_rules_values: Some(complex_id_rules.values), - hostname_hide_index: Some(hostname_hide.keys), - hostname_hide_values: Some(hostname_hide.values), - hostname_inject_script_index: Some(hostname_inject_script.keys), - hostname_inject_script_values: Some(hostname_inject_script.values), - hostname_index: Some(hostname_specific_rules.keys), - hostname_values: Some(hostname_specific_rules.values), - }, - ) - } -} - /// Representations of filters with complex behavior that relies on in-page JS logic. /// /// These get stored in-memory as JSON and should be deserialized/acted on by a content script. @@ -593,30 +118,6 @@ impl ProceduralOrActionFilter { } } -/// Exists to use common logic for binning filters correctly -#[derive(Clone)] -enum SpecificFilterType { - Hide(String), - Unhide(String), - InjectScript((String, PermissionMask)), - UninjectScript((String, PermissionMask)), - ProceduralOrAction(String), - ProceduralOrActionException(String), -} - -impl SpecificFilterType { - fn negated(self) -> Self { - match self { - Self::Hide(s) => Self::Unhide(s), - Self::Unhide(s) => Self::Hide(s), - Self::InjectScript(s) => Self::UninjectScript(s), - Self::UninjectScript(s) => Self::InjectScript(s), - Self::ProceduralOrAction(s) => Self::ProceduralOrActionException(s), - Self::ProceduralOrActionException(s) => Self::ProceduralOrAction(s), - } - } -} - fn hostname_domain_hashes(hostname: &str, domain: &str) -> (Vec, Vec) { let request_entities = crate::filters::cosmetic::get_entity_hashes_from_labels(hostname, domain); @@ -626,62 +127,233 @@ fn hostname_domain_hashes(hostname: &str, domain: &str) -> (Vec, Vec (request_entities, request_hostnames) } -/// Returns the first token of a CSS selector. -/// -/// This should only be called once `selector` has been verified to start with either a "#" or "." -/// character. -fn key_from_selector(selector: &str) -> Option { - use once_cell::sync::Lazy; - use regex::Regex; - - static RE_PLAIN_SELECTOR: Lazy = Lazy::new(|| Regex::new(r"^[#.][\w\\-]+").unwrap()); - static RE_PLAIN_SELECTOR_ESCAPED: Lazy = - Lazy::new(|| Regex::new(r"^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+").unwrap()); - static RE_ESCAPE_SEQUENCE: Lazy = - Lazy::new(|| Regex::new(r"\\([0-9A-Fa-f]+ |.)").unwrap()); - - // If there are no escape characters in the selector, just take the first class or id token. - let mat = RE_PLAIN_SELECTOR.find(selector); - if let Some(location) = mat { - let key = &location.as_str(); - if find_char(b'\\', key.as_bytes()).is_none() { - return Some((*key).into()); - } - } else { - return None; - } - // Otherwise, the characters in the selector must be escaped. - let mat = RE_PLAIN_SELECTOR_ESCAPED.find(selector); - if let Some(location) = mat { - let mut key = String::with_capacity(selector.len()); - let escaped = &location.as_str(); - let mut beginning = 0; - let mat = RE_ESCAPE_SEQUENCE.captures_iter(escaped); - for capture in mat { - // Unwrap is safe because the 0th capture group is the match itself - let location = capture.get(0).unwrap(); - key += &escaped[beginning..location.start()]; - beginning = location.end(); - // Unwrap is safe because there is a capture group specified in the regex - let capture = capture.get(1).unwrap().as_str(); - if capture.chars().count() == 1 { - // Check number of unicode characters rather than byte length - key += capture; - } else { - // This u32 conversion can overflow - let codepoint = u32::from_str_radix(&capture[..capture.len() - 1], 16).ok()?; - - // Not all u32s are valid Unicode codepoints - key += &core::char::from_u32(codepoint)?.to_string(); - } - } - Some(key + &escaped[beginning..]) - } else { - None - } +impl CosmeticFilterCache { + pub fn from_context(filter_data_context: FilterDataContextRef) -> Self { + Self { + filter_data_context, + } + } + + #[cfg(test)] + pub fn from_rules(rules: Vec) -> Self { + use crate::filters::{ + fb_builder::make_flatbuffer, fb_network::FilterDataContext, + }; + + let memory = make_flatbuffer(vec![], rules, true, 0); + + let filter_data_context = FilterDataContext::new(memory); + Self::from_context(filter_data_context) + } + + /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they + /// apply to all sites. Rather than injecting all of these rules onto every page, which would + /// blow up memory usage, we only inject rules based on classes and ids that actually appear on + /// the page (in practice, a `MutationObserver` is used to identify those elements). We can + /// include rules like `.a-class div#ads > .advertisement`, keyed by the `.a-class` selector, + /// since we know that this rule cannot possibly apply unless there is an `.a-class` element on + /// the page. + /// + /// This method returns all of the generic CSS selectors of elements to hide (i.e. with a + /// `display: none !important` CSS rule) that could possibly be or become relevant to the page + /// given the new classes and ids that have appeared on the page. It guarantees that it will be + /// safe to hide those elements on a particular page by taking into account the page's + /// hostname-specific set of exception rules. + /// + /// The exceptions should be returned directly as they appear in the page's + /// `UrlSpecificResources`. The exceptions, along with the set of already-seen classes and ids, + /// must be cached externally as the cosmetic filtering subsystem here is designed to be + /// stateless with regard to active page sessions. + pub fn hidden_class_id_selectors( + &self, + classes: impl IntoIterator>, + ids: impl IntoIterator>, + exceptions: &HashSet, + ) -> Vec { + let mut selectors = vec![]; + + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); + let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); + let complex_class_rules = FlatMapStringView::new( + cs.complex_class_rules_index(), + cs.complex_class_rules_values(), + ); + let complex_id_rules = + FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); + + classes.into_iter().for_each(|class| { + let class = class.as_ref(); + if simple_class_rules.contains(class) && !exceptions.contains(&format!(".{}", class)) { + selectors.push(format!(".{}", class)); + } + if let Some(bucket) = complex_class_rules.get(class) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } + } + }); + ids.into_iter().for_each(|id| { + let id = id.as_ref(); + if simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { + selectors.push(format!("#{}", id)); + } + if let Some(bucket) = complex_id_rules.get(id) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } + } + }); + + selectors + } + + /// Any rules that can't be handled by `hidden_class_id_selectors` are returned by + /// `hostname_cosmetic_resources`. As soon as a page navigation is committed, this method + /// should be queried to get the initial set of cosmetic filtering operations to apply to the + /// page. This provides any rules specifying elements to hide by selectors that are too complex + /// to be returned by `hidden_class_id_selectors` (i.e. not directly starting with a class or + /// id selector, like `div[class*="Ads"]`), or any rule that is only applicable to a particular + /// hostname or set of hostnames (like `example.com##.a-class`). The first category is always + /// injected into every page, and makes up a relatively small number of rules in practice. + pub fn hostname_cosmetic_resources( + &self, + resources: &ResourceStorage, + hostname: &str, + generichide: bool, + ) -> UrlSpecificResources { + let domain_str = { + let (start, end) = crate::url_parser::get_host_domain(hostname); + &hostname[start..end] + }; + + let (request_entities, request_hostnames) = hostname_domain_hashes(hostname, domain_str); + + let mut specific_hide_selectors = HashSet::new(); + let mut procedural_actions = HashSet::new(); + let mut script_injections = HashMap::<&str, PermissionMask>::new(); + let mut exceptions = HashSet::new(); + + let mut except_all_scripts = false; + + let hashes: Vec<&Hash> = request_entities + .iter() + .chain(request_hostnames.iter()) + .collect(); + + let cf = self.filter_data_context.memory.root().cosmetic_filters(); + let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); + let hostname_hide_view = + FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); + let hostname_inject_script_view = FlatMultiMapView::new( + cf.hostname_inject_script_index(), + cf.hostname_inject_script_values(), + ); + + for hash in hashes.iter() { + // Handle top-level hide selectors + if let Some(hide_iterator) = hostname_hide_view.get(**hash) { + for (_, hide_selector) in hide_iterator { + if !exceptions.contains(hide_selector) { + specific_hide_selectors.insert(hide_selector.to_owned()); + } + } + } + + // Handle top-level inject scripts with encoded permissions + if let Some(script_iterator) = hostname_inject_script_view.get(**hash) { + for (_, encoded_script) in script_iterator { + let (permission, script) = decode_script_with_permission(encoded_script); + script_injections + .entry(script) + .and_modify(|entry| *entry |= permission) + .or_insert(permission); + } + } + + // Handle remaining rule types from HostnameSpecificRules + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process procedural actions + if let Some(procedural_actions_rules) = hostname_rules.procedural_action() { + for action in procedural_actions_rules.iter() { + procedural_actions.insert(action.to_owned()); + } + } + } + } + + // Process unhide/exception filters + for hash in hashes.iter() { + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process unhide selectors (special behavior: they also go in exceptions) + if let Some(unhide_rules) = hostname_rules.unhide() { + for selector in unhide_rules.iter() { + specific_hide_selectors.remove(selector); + exceptions.insert(selector.to_owned()); + } + } + + // Process procedural action exceptions + if let Some(procedural_exceptions) = hostname_rules.procedural_action_exception() { + for action in procedural_exceptions.iter() { + procedural_actions.remove(action); + } + } + + // Process script uninjects + if let Some(uninject_scripts) = hostname_rules.uninject_script() { + for script in uninject_scripts.iter() { + if script.is_empty() { + except_all_scripts = true; + script_injections.clear(); + } + if except_all_scripts { + continue; + } + script_injections.remove(script); + } + } + } + } + + let hide_selectors = if generichide { + specific_hide_selectors + } else { + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let misc_generic_selectors_vector = cs.misc_generic_selectors(); + + // TODO: check performance of this + let mut hide_selectors = HashSet::new(); + for i in 0..misc_generic_selectors_vector.len() { + let selector = misc_generic_selectors_vector.get(i); + if !exceptions.contains(selector) { + hide_selectors.insert(selector.to_string()); + } + } + specific_hide_selectors.into_iter().for_each(|sel| { + hide_selectors.insert(sel); + }); + hide_selectors + }; + + let injected_script = resources.get_scriptlet_resources(script_injections); + + UrlSpecificResources { + hide_selectors, + procedural_actions, + exceptions, + injected_script, + generichide, + } + } } + #[cfg(test)] #[path = "../tests/unit/cosmetic_filter_cache.rs"] mod unit_tests; diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs new file mode 100644 index 00000000..470fa3cd --- /dev/null +++ b/src/cosmetic_filter_cache_builder.rs @@ -0,0 +1,247 @@ + +use crate::cosmetic_filter_cache::ProceduralOrActionFilter; +use crate::cosmetic_filter_utils::SpecificFilterType; +use crate::filters::cosmetic::{ + CosmeticFilter, CosmeticFilterMask, CosmeticFilterOperator}; +use crate::cosmetic_filter_utils::{encode_script_with_permission, key_from_selector}; +use crate::filters::fb_network::flat::fb; +use crate::flatbuffers::containers::flat_map::FlatMapBuilder; +use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; + +use crate::flatbuffers::containers::flat_serialize::{ + serialize_vec_opt, FlatBuilder, FlatSerialize, +}; + +use crate::utils::Hash; + +use std::collections::{HashMap, HashSet}; + + + +use flatbuffers::WIPOffset; + +/// Accumulates hostname-specific rules for a single domain before building HostnameSpecificRules +/// Note: hide and inject_script are now handled separately at the top level +#[derive(Default)] +struct HostnameRule { + unhide: Vec, + uninject_script: Vec, + procedural_action: Vec, + procedural_action_exception: Vec, +} + +impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for HostnameRule { + type Output = WIPOffset>; + + fn serialize( + value: Self, + builder: &mut B, + ) -> flatbuffers::WIPOffset> { + let unhide = serialize_vec_opt(value.unhide, builder); + let uninject_script = serialize_vec_opt(value.uninject_script, builder); + let procedural_action = serialize_vec_opt(value.procedural_action, builder); + let procedural_action_exception = + serialize_vec_opt(value.procedural_action_exception, builder); + + fb::HostnameSpecificRules::create( + builder.raw_builder(), + &fb::HostnameSpecificRulesArgs { + unhide, + uninject_script, + procedural_action, + procedural_action_exception, + }, + ) + } +} + +#[derive(Default)] +pub(crate) struct CosmeticFilterCacheBuilder { + simple_class_rules: HashSet, + simple_id_rules: HashSet, + misc_generic_selectors: HashSet, + complex_class_rules: FlatMultiMapBuilder, + complex_id_rules: FlatMultiMapBuilder, + + hostname_hide: FlatMultiMapBuilder, + hostname_inject_script: FlatMultiMapBuilder, + + specific_rules: HashMap, +} + +impl CosmeticFilterCacheBuilder { + pub fn from_rules(rules: Vec) -> Self { + let mut self_ = Self::default(); + + for rule in rules { + self_.add_filter(rule) + } + + self_ + } + + pub fn add_filter(&mut self, rule: CosmeticFilter) { + if rule.has_hostname_constraint() { + if let Some(generic_rule) = rule.hidden_generic_rule() { + self.add_generic_filter(generic_rule); + } + self.store_hostname_rule(rule); + } else { + self.add_generic_filter(rule); + } + } + + /// Add a filter, assuming it has already been determined to be a generic rule + fn add_generic_filter(&mut self, rule: CosmeticFilter) { + let selector = match rule.plain_css_selector() { + Some(s) => s.to_string(), + None => { + // Procedural cosmetic filters cannot be generic. + // Silently ignoring this filter. + return; + } + }; + + if selector.starts_with('.') { + if let Some(key) = key_from_selector(&selector) { + assert!(key.starts_with('.')); + let class = key[1..].to_string(); + if key == selector { + self.simple_class_rules.insert(class); + } else { + self.complex_class_rules.insert(class, selector); + } + } + } else if selector.starts_with('#') { + if let Some(key) = key_from_selector(&selector) { + assert!(key.starts_with('#')); + let id = key[1..].to_string(); + if key == selector { + self.simple_id_rules.insert(id); + } else { + self.complex_id_rules.insert(id, selector); + } + } + } else { + self.misc_generic_selectors.insert(selector); + } + } + + // TODO: review this + fn store_hostname_rule(&mut self, rule: CosmeticFilter) { + use SpecificFilterType::*; + + let unhide = rule.mask.contains(CosmeticFilterMask::UNHIDE); + let script_inject = rule.mask.contains(CosmeticFilterMask::SCRIPT_INJECT); + + let kind = match ( + script_inject, + rule.plain_css_selector().map(|s| s.to_string()), + rule.action, + ) { + (false, Some(selector), None) => Hide(selector), + (true, Some(selector), None) => InjectScript((selector, rule.permission)), + (false, selector, action) => ProceduralOrAction( + serde_json::to_string(&ProceduralOrActionFilter { + selector: selector + .map(|selector| vec![CosmeticFilterOperator::CssSelector(selector)]) + .unwrap_or(rule.selector), + action, + }) + .unwrap(), + ), + (true, _, Some(_)) => return, // script injection with action - shouldn't be possible + (true, None, _) => return, // script injection without plain CSS selector - shouldn't be possible + }; + + let kind = if unhide { kind.negated() } else { kind }; + + let tokens_to_insert = std::iter::empty() + .chain(rule.hostnames.unwrap_or_default()) + .chain(rule.entities.unwrap_or_default()); + + tokens_to_insert.for_each(|t| self.store_hostname_filter(&t, kind.clone())); + + let tokens_to_insert_negated = std::iter::empty() + .chain(rule.not_hostnames.unwrap_or_default()) + .chain(rule.not_entities.unwrap_or_default()); + + let negated = kind.negated(); + + tokens_to_insert_negated.for_each(|t| self.store_hostname_filter(&t, negated.clone())); + } + + fn store_hostname_filter(&mut self, token: &Hash, kind: SpecificFilterType) { + use SpecificFilterType::*; + + match kind { + // Handle hide and inject_script at top level for better deduplication + Hide(s) => { + self.hostname_hide.insert(*token, s); + } + InjectScript((s, permission)) => { + let encoded_script = encode_script_with_permission(s, permission); + self.hostname_inject_script.insert(*token, encoded_script); + } + // Handle remaining types through HostnameRule + Unhide(s) => { + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + entry.unhide.push(s); + } + UninjectScript((s, _)) => { + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + entry.uninject_script.push(s); + } + ProceduralOrAction(s) => { + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + entry.procedural_action.push(s); + } + ProceduralOrActionException(s) => { + let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + entry.procedural_action_exception.push(s); + } + } + } +} + +impl<'a, B: FlatBuilder<'a>> FlatSerialize<'a, B> for CosmeticFilterCacheBuilder { + type Output = WIPOffset>; + fn serialize(value: Self, builder: &mut B) -> WIPOffset> { + let complex_class_rules = FlatMultiMapBuilder::finish(value.complex_class_rules, builder); + let complex_id_rules = FlatMultiMapBuilder::finish(value.complex_id_rules, builder); + + // Handle top-level hostname hide and inject_script for better deduplication + let hostname_hide = FlatMultiMapBuilder::finish(value.hostname_hide, builder); + let hostname_inject_script = + FlatMultiMapBuilder::finish(value.hostname_inject_script, builder); + + // Handle remaining rule types through HostnameSpecificRules + let hostname_specific_rules = FlatMapBuilder::finish(value.specific_rules, builder); + + let simple_class_rules = Some(FlatSerialize::serialize(value.simple_class_rules, builder)); + let simple_id_rules = Some(FlatSerialize::serialize(value.simple_id_rules, builder)); + let misc_generic_selectors = Some(FlatSerialize::serialize( + value.misc_generic_selectors, + builder, + )); + + fb::CosmeticFilters::create( + builder.raw_builder(), + &fb::CosmeticFiltersArgs { + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index: Some(complex_class_rules.keys), + complex_class_rules_values: Some(complex_class_rules.values), + complex_id_rules_index: Some(complex_id_rules.keys), + complex_id_rules_values: Some(complex_id_rules.values), + hostname_hide_index: Some(hostname_hide.keys), + hostname_hide_values: Some(hostname_hide.values), + hostname_inject_script_index: Some(hostname_inject_script.keys), + hostname_inject_script_values: Some(hostname_inject_script.values), + hostname_index: Some(hostname_specific_rules.keys), + hostname_values: Some(hostname_specific_rules.values), + }, + ) + } +} diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs new file mode 100644 index 00000000..7643c9f8 --- /dev/null +++ b/src/cosmetic_filter_utils.rs @@ -0,0 +1,104 @@ +use crate::resources::PermissionMask; +use memchr::memchr as find_char; + +/// Returns the first token of a CSS selector. +/// +/// This should only be called once `selector` has been verified to start with either a "#" or "." +/// character. +pub(crate) fn key_from_selector(selector: &str) -> Option { + use once_cell::sync::Lazy; + use regex::Regex; + + static RE_PLAIN_SELECTOR: Lazy = Lazy::new(|| Regex::new(r"^[#.][\w\\-]+").unwrap()); + static RE_PLAIN_SELECTOR_ESCAPED: Lazy = + Lazy::new(|| Regex::new(r"^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+").unwrap()); + static RE_ESCAPE_SEQUENCE: Lazy = + Lazy::new(|| Regex::new(r"\\([0-9A-Fa-f]+ |.)").unwrap()); + + // If there are no escape characters in the selector, just take the first class or id token. + let mat = RE_PLAIN_SELECTOR.find(selector); + if let Some(location) = mat { + let key = &location.as_str(); + if find_char(b'\\', key.as_bytes()).is_none() { + return Some((*key).into()); + } + } else { + return None; + } + + // Otherwise, the characters in the selector must be escaped. + let mat = RE_PLAIN_SELECTOR_ESCAPED.find(selector); + if let Some(location) = mat { + let mut key = String::with_capacity(selector.len()); + let escaped = &location.as_str(); + let mut beginning = 0; + let mat = RE_ESCAPE_SEQUENCE.captures_iter(escaped); + for capture in mat { + // Unwrap is safe because the 0th capture group is the match itself + let location = capture.get(0).unwrap(); + key += &escaped[beginning..location.start()]; + beginning = location.end(); + // Unwrap is safe because there is a capture group specified in the regex + let capture = capture.get(1).unwrap().as_str(); + if capture.chars().count() == 1 { + // Check number of unicode characters rather than byte length + key += capture; + } else { + // This u32 conversion can overflow + let codepoint = u32::from_str_radix(&capture[..capture.len() - 1], 16).ok()?; + + // Not all u32s are valid Unicode codepoints + key += &core::char::from_u32(codepoint)?.to_string(); + } + } + Some(key + &escaped[beginning..]) + } else { + None + } +} + + +/// Exists to use common logic for binning filters correctly +#[derive(Clone)] +pub(crate) enum SpecificFilterType { + Hide(String), + Unhide(String), + InjectScript((String, PermissionMask)), + UninjectScript((String, PermissionMask)), + ProceduralOrAction(String), + ProceduralOrActionException(String), +} + +impl SpecificFilterType { + pub(crate) fn negated(self) -> Self { + match self { + Self::Hide(s) => Self::Unhide(s), + Self::Unhide(s) => Self::Hide(s), + Self::InjectScript(s) => Self::UninjectScript(s), + Self::UninjectScript(s) => Self::InjectScript(s), + Self::ProceduralOrAction(s) => Self::ProceduralOrActionException(s), + Self::ProceduralOrActionException(s) => Self::ProceduralOrAction(s), + } + } +} + +/// Encodes permission bits in the last byte of a script string +/// Returns the script with permission byte prepended +pub(crate) fn encode_script_with_permission(mut script: String, permission: PermissionMask) -> String { + script.push(permission.to_bits() as char); + script +} + +/// Decodes permission bits from the last byte of a script string +/// Returns (permission, script) tuple +pub(crate) fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { + if encoded_script.is_empty() { + return (PermissionMask::default(), encoded_script); + } + + let last_char = encoded_script.chars().last().unwrap(); + let permission_bits = last_char as u8; + let permission = PermissionMask::from_bits(permission_bits); + let script = &encoded_script[..encoded_script.len() - 1]; + (permission, script) +} diff --git a/src/engine.rs b/src/engine.rs index b2020bbe..7afbe6be 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,7 +2,7 @@ use crate::blocker::{Blocker, BlockerResult}; use crate::cosmetic_filter_cache::{CosmeticFilterCache, UrlSpecificResources}; -use crate::filters::fb_builder::make_flatbuffer_from_rules; +use crate::filters::fb_builder::make_flatbuffer; use crate::filters::fb_network::{FilterDataContext, FilterDataContextRef}; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; use crate::lists::{FilterSet, ParseOptions}; @@ -115,7 +115,7 @@ impl Engine { .. } = set; - let memory = make_flatbuffer_from_rules( + let memory = make_flatbuffer( network_filters, cosmetic_filters, optimize, diff --git a/src/filters/mod.rs b/src/filters/mod.rs index c1702a2d..976377c9 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -6,4 +6,5 @@ mod network_matchers; pub mod cosmetic; pub(crate) mod fb_builder; pub(crate) mod fb_network; +pub(crate) mod fb_network_builder; pub mod network; diff --git a/src/lib.rs b/src/lib.rs index 0d95e5f0..df02fa99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ pub mod blocker; #[cfg(feature = "content-blocking")] pub mod content_blocking; pub mod cosmetic_filter_cache; +mod cosmetic_filter_cache_builder; +mod cosmetic_filter_utils; mod engine; pub mod filters; mod flatbuffers; diff --git a/tests/unit/cosmetic_filter_cache.rs b/tests/unit/cosmetic_filter_cache.rs index 7af960dc..7d2be339 100644 --- a/tests/unit/cosmetic_filter_cache.rs +++ b/tests/unit/cosmetic_filter_cache.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod key_from_selector_tests { - use super::super::key_from_selector; + use crate::cosmetic_filter_utils::key_from_selector; #[test] fn no_escapes() { From 28f6c9b96b51445ce121591bbc7bc3096a14d8cc Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 25 Aug 2025 21:38:34 +0700 Subject: [PATCH 07/21] cargo fmt --- src/blocker.rs | 2 +- src/cosmetic_filter_cache.rs | 452 +- src/cosmetic_filter_cache_builder.rs | 28 +- src/cosmetic_filter_utils.rs | 116 +- src/filters/fb_builder.rs | 4 +- src/filters/fb_network.rs | 7 +- src/filters/fb_network_builder.rs | 496 +-- .../fb_network_filter_generated.rs | 3619 ++++++++++------- 8 files changed, 2727 insertions(+), 1997 deletions(-) diff --git a/src/blocker.rs b/src/blocker.rs index 96424004..21b50bc1 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -6,8 +6,8 @@ use serde::Serialize; use std::collections::HashSet; use std::ops::DerefMut; -use crate::filters::fb_network_builder::NetworkFilterListId; use crate::filters::fb_network::FilterDataContextRef; +use crate::filters::fb_network_builder::NetworkFilterListId; use crate::filters::network::NetworkFilterMaskHelper; use crate::network_filter_list::NetworkFilterList; use crate::regex_manager::{RegexManager, RegexManagerDiscardPolicy}; diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 53194fc9..ffe57aff 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -11,15 +11,11 @@ use crate::cosmetic_filter_utils::decode_script_with_permission; #[cfg(test)] use crate::filters::cosmetic::CosmeticFilter; -use crate::filters::cosmetic::{ - CosmeticFilterAction, CosmeticFilterOperator, -}; +use crate::filters::cosmetic::{CosmeticFilterAction, CosmeticFilterOperator}; use crate::filters::fb_network::FilterDataContextRef; use crate::flatbuffers::containers::flat_map::FlatMapView; -use crate::flatbuffers::containers::flat_multimap::{ - FlatMapStringView, FlatMultiMapView, -}; +use crate::flatbuffers::containers::flat_multimap::{FlatMapStringView, FlatMultiMapView}; use crate::flatbuffers::containers::flat_set::FlatSetView; use crate::resources::{PermissionMask, ResourceStorage}; @@ -127,232 +123,228 @@ fn hostname_domain_hashes(hostname: &str, domain: &str) -> (Vec, Vec (request_entities, request_hostnames) } - impl CosmeticFilterCache { - pub fn from_context(filter_data_context: FilterDataContextRef) -> Self { - Self { - filter_data_context, - } - } - - #[cfg(test)] - pub fn from_rules(rules: Vec) -> Self { - use crate::filters::{ - fb_builder::make_flatbuffer, fb_network::FilterDataContext, - }; - - let memory = make_flatbuffer(vec![], rules, true, 0); - - let filter_data_context = FilterDataContext::new(memory); - Self::from_context(filter_data_context) - } - - /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they - /// apply to all sites. Rather than injecting all of these rules onto every page, which would - /// blow up memory usage, we only inject rules based on classes and ids that actually appear on - /// the page (in practice, a `MutationObserver` is used to identify those elements). We can - /// include rules like `.a-class div#ads > .advertisement`, keyed by the `.a-class` selector, - /// since we know that this rule cannot possibly apply unless there is an `.a-class` element on - /// the page. - /// - /// This method returns all of the generic CSS selectors of elements to hide (i.e. with a - /// `display: none !important` CSS rule) that could possibly be or become relevant to the page - /// given the new classes and ids that have appeared on the page. It guarantees that it will be - /// safe to hide those elements on a particular page by taking into account the page's - /// hostname-specific set of exception rules. - /// - /// The exceptions should be returned directly as they appear in the page's - /// `UrlSpecificResources`. The exceptions, along with the set of already-seen classes and ids, - /// must be cached externally as the cosmetic filtering subsystem here is designed to be - /// stateless with regard to active page sessions. - pub fn hidden_class_id_selectors( - &self, - classes: impl IntoIterator>, - ids: impl IntoIterator>, - exceptions: &HashSet, - ) -> Vec { - let mut selectors = vec![]; - - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); - let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); - let complex_class_rules = FlatMapStringView::new( - cs.complex_class_rules_index(), - cs.complex_class_rules_values(), - ); - let complex_id_rules = - FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); - - classes.into_iter().for_each(|class| { - let class = class.as_ref(); - if simple_class_rules.contains(class) && !exceptions.contains(&format!(".{}", class)) { - selectors.push(format!(".{}", class)); - } - if let Some(bucket) = complex_class_rules.get(class) { - for (_, sel) in bucket { - if !exceptions.contains(sel) { - selectors.push(sel.to_string()); - } - } - } - }); - ids.into_iter().for_each(|id| { - let id = id.as_ref(); - if simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { - selectors.push(format!("#{}", id)); - } - if let Some(bucket) = complex_id_rules.get(id) { - for (_, sel) in bucket { - if !exceptions.contains(sel) { - selectors.push(sel.to_string()); - } - } - } - }); - - selectors - } - - /// Any rules that can't be handled by `hidden_class_id_selectors` are returned by - /// `hostname_cosmetic_resources`. As soon as a page navigation is committed, this method - /// should be queried to get the initial set of cosmetic filtering operations to apply to the - /// page. This provides any rules specifying elements to hide by selectors that are too complex - /// to be returned by `hidden_class_id_selectors` (i.e. not directly starting with a class or - /// id selector, like `div[class*="Ads"]`), or any rule that is only applicable to a particular - /// hostname or set of hostnames (like `example.com##.a-class`). The first category is always - /// injected into every page, and makes up a relatively small number of rules in practice. - pub fn hostname_cosmetic_resources( - &self, - resources: &ResourceStorage, - hostname: &str, - generichide: bool, - ) -> UrlSpecificResources { - let domain_str = { - let (start, end) = crate::url_parser::get_host_domain(hostname); - &hostname[start..end] - }; - - let (request_entities, request_hostnames) = hostname_domain_hashes(hostname, domain_str); - - let mut specific_hide_selectors = HashSet::new(); - let mut procedural_actions = HashSet::new(); - let mut script_injections = HashMap::<&str, PermissionMask>::new(); - let mut exceptions = HashSet::new(); - - let mut except_all_scripts = false; - - let hashes: Vec<&Hash> = request_entities - .iter() - .chain(request_hostnames.iter()) - .collect(); - - let cf = self.filter_data_context.memory.root().cosmetic_filters(); - let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); - let hostname_hide_view = - FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); - let hostname_inject_script_view = FlatMultiMapView::new( - cf.hostname_inject_script_index(), - cf.hostname_inject_script_values(), - ); - - for hash in hashes.iter() { - // Handle top-level hide selectors - if let Some(hide_iterator) = hostname_hide_view.get(**hash) { - for (_, hide_selector) in hide_iterator { - if !exceptions.contains(hide_selector) { - specific_hide_selectors.insert(hide_selector.to_owned()); - } - } - } - - // Handle top-level inject scripts with encoded permissions - if let Some(script_iterator) = hostname_inject_script_view.get(**hash) { - for (_, encoded_script) in script_iterator { - let (permission, script) = decode_script_with_permission(encoded_script); - script_injections - .entry(script) - .and_modify(|entry| *entry |= permission) - .or_insert(permission); - } - } - - // Handle remaining rule types from HostnameSpecificRules - if let Some(hostname_rules) = hostname_rules_view.get(**hash) { - // Process procedural actions - if let Some(procedural_actions_rules) = hostname_rules.procedural_action() { - for action in procedural_actions_rules.iter() { - procedural_actions.insert(action.to_owned()); - } - } - } - } - - // Process unhide/exception filters - for hash in hashes.iter() { - if let Some(hostname_rules) = hostname_rules_view.get(**hash) { - // Process unhide selectors (special behavior: they also go in exceptions) - if let Some(unhide_rules) = hostname_rules.unhide() { - for selector in unhide_rules.iter() { - specific_hide_selectors.remove(selector); - exceptions.insert(selector.to_owned()); - } - } - - // Process procedural action exceptions - if let Some(procedural_exceptions) = hostname_rules.procedural_action_exception() { - for action in procedural_exceptions.iter() { - procedural_actions.remove(action); - } - } - - // Process script uninjects - if let Some(uninject_scripts) = hostname_rules.uninject_script() { - for script in uninject_scripts.iter() { - if script.is_empty() { - except_all_scripts = true; - script_injections.clear(); - } - if except_all_scripts { - continue; - } - script_injections.remove(script); - } - } - } - } - - let hide_selectors = if generichide { - specific_hide_selectors - } else { - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let misc_generic_selectors_vector = cs.misc_generic_selectors(); - - // TODO: check performance of this - let mut hide_selectors = HashSet::new(); - for i in 0..misc_generic_selectors_vector.len() { - let selector = misc_generic_selectors_vector.get(i); - if !exceptions.contains(selector) { - hide_selectors.insert(selector.to_string()); - } - } - specific_hide_selectors.into_iter().for_each(|sel| { - hide_selectors.insert(sel); - }); - hide_selectors - }; - - let injected_script = resources.get_scriptlet_resources(script_injections); - - UrlSpecificResources { - hide_selectors, - procedural_actions, - exceptions, - injected_script, - generichide, - } - } -} + pub fn from_context(filter_data_context: FilterDataContextRef) -> Self { + Self { + filter_data_context, + } + } + + #[cfg(test)] + pub fn from_rules(rules: Vec) -> Self { + use crate::filters::{fb_builder::make_flatbuffer, fb_network::FilterDataContext}; + + let memory = make_flatbuffer(vec![], rules, true, 0); + + let filter_data_context = FilterDataContext::new(memory); + Self::from_context(filter_data_context) + } + + /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they + /// apply to all sites. Rather than injecting all of these rules onto every page, which would + /// blow up memory usage, we only inject rules based on classes and ids that actually appear on + /// the page (in practice, a `MutationObserver` is used to identify those elements). We can + /// include rules like `.a-class div#ads > .advertisement`, keyed by the `.a-class` selector, + /// since we know that this rule cannot possibly apply unless there is an `.a-class` element on + /// the page. + /// + /// This method returns all of the generic CSS selectors of elements to hide (i.e. with a + /// `display: none !important` CSS rule) that could possibly be or become relevant to the page + /// given the new classes and ids that have appeared on the page. It guarantees that it will be + /// safe to hide those elements on a particular page by taking into account the page's + /// hostname-specific set of exception rules. + /// + /// The exceptions should be returned directly as they appear in the page's + /// `UrlSpecificResources`. The exceptions, along with the set of already-seen classes and ids, + /// must be cached externally as the cosmetic filtering subsystem here is designed to be + /// stateless with regard to active page sessions. + pub fn hidden_class_id_selectors( + &self, + classes: impl IntoIterator>, + ids: impl IntoIterator>, + exceptions: &HashSet, + ) -> Vec { + let mut selectors = vec![]; + + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); + let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); + let complex_class_rules = FlatMapStringView::new( + cs.complex_class_rules_index(), + cs.complex_class_rules_values(), + ); + let complex_id_rules = + FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); + + classes.into_iter().for_each(|class| { + let class = class.as_ref(); + if simple_class_rules.contains(class) && !exceptions.contains(&format!(".{}", class)) { + selectors.push(format!(".{}", class)); + } + if let Some(bucket) = complex_class_rules.get(class) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } + } + }); + ids.into_iter().for_each(|id| { + let id = id.as_ref(); + if simple_id_rules.contains(id) && !exceptions.contains(&format!("#{}", id)) { + selectors.push(format!("#{}", id)); + } + if let Some(bucket) = complex_id_rules.get(id) { + for (_, sel) in bucket { + if !exceptions.contains(sel) { + selectors.push(sel.to_string()); + } + } + } + }); + selectors + } + + /// Any rules that can't be handled by `hidden_class_id_selectors` are returned by + /// `hostname_cosmetic_resources`. As soon as a page navigation is committed, this method + /// should be queried to get the initial set of cosmetic filtering operations to apply to the + /// page. This provides any rules specifying elements to hide by selectors that are too complex + /// to be returned by `hidden_class_id_selectors` (i.e. not directly starting with a class or + /// id selector, like `div[class*="Ads"]`), or any rule that is only applicable to a particular + /// hostname or set of hostnames (like `example.com##.a-class`). The first category is always + /// injected into every page, and makes up a relatively small number of rules in practice. + pub fn hostname_cosmetic_resources( + &self, + resources: &ResourceStorage, + hostname: &str, + generichide: bool, + ) -> UrlSpecificResources { + let domain_str = { + let (start, end) = crate::url_parser::get_host_domain(hostname); + &hostname[start..end] + }; + + let (request_entities, request_hostnames) = hostname_domain_hashes(hostname, domain_str); + + let mut specific_hide_selectors = HashSet::new(); + let mut procedural_actions = HashSet::new(); + let mut script_injections = HashMap::<&str, PermissionMask>::new(); + let mut exceptions = HashSet::new(); + + let mut except_all_scripts = false; + + let hashes: Vec<&Hash> = request_entities + .iter() + .chain(request_hostnames.iter()) + .collect(); + + let cf = self.filter_data_context.memory.root().cosmetic_filters(); + let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); + let hostname_hide_view = + FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); + let hostname_inject_script_view = FlatMultiMapView::new( + cf.hostname_inject_script_index(), + cf.hostname_inject_script_values(), + ); + + for hash in hashes.iter() { + // Handle top-level hide selectors + if let Some(hide_iterator) = hostname_hide_view.get(**hash) { + for (_, hide_selector) in hide_iterator { + if !exceptions.contains(hide_selector) { + specific_hide_selectors.insert(hide_selector.to_owned()); + } + } + } + + // Handle top-level inject scripts with encoded permissions + if let Some(script_iterator) = hostname_inject_script_view.get(**hash) { + for (_, encoded_script) in script_iterator { + let (permission, script) = decode_script_with_permission(encoded_script); + script_injections + .entry(script) + .and_modify(|entry| *entry |= permission) + .or_insert(permission); + } + } + + // Handle remaining rule types from HostnameSpecificRules + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process procedural actions + if let Some(procedural_actions_rules) = hostname_rules.procedural_action() { + for action in procedural_actions_rules.iter() { + procedural_actions.insert(action.to_owned()); + } + } + } + } + + // Process unhide/exception filters + for hash in hashes.iter() { + if let Some(hostname_rules) = hostname_rules_view.get(**hash) { + // Process unhide selectors (special behavior: they also go in exceptions) + if let Some(unhide_rules) = hostname_rules.unhide() { + for selector in unhide_rules.iter() { + specific_hide_selectors.remove(selector); + exceptions.insert(selector.to_owned()); + } + } + + // Process procedural action exceptions + if let Some(procedural_exceptions) = hostname_rules.procedural_action_exception() { + for action in procedural_exceptions.iter() { + procedural_actions.remove(action); + } + } + + // Process script uninjects + if let Some(uninject_scripts) = hostname_rules.uninject_script() { + for script in uninject_scripts.iter() { + if script.is_empty() { + except_all_scripts = true; + script_injections.clear(); + } + if except_all_scripts { + continue; + } + script_injections.remove(script); + } + } + } + } + + let hide_selectors = if generichide { + specific_hide_selectors + } else { + let cs = self.filter_data_context.memory.root().cosmetic_filters(); + let misc_generic_selectors_vector = cs.misc_generic_selectors(); + + // TODO: check performance of this + let mut hide_selectors = HashSet::new(); + for i in 0..misc_generic_selectors_vector.len() { + let selector = misc_generic_selectors_vector.get(i); + if !exceptions.contains(selector) { + hide_selectors.insert(selector.to_string()); + } + } + specific_hide_selectors.into_iter().for_each(|sel| { + hide_selectors.insert(sel); + }); + hide_selectors + }; + + let injected_script = resources.get_scriptlet_resources(script_injections); + + UrlSpecificResources { + hide_selectors, + procedural_actions, + exceptions, + injected_script, + generichide, + } + } +} #[cfg(test)] #[path = "../tests/unit/cosmetic_filter_cache.rs"] diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs index 470fa3cd..61c91111 100644 --- a/src/cosmetic_filter_cache_builder.rs +++ b/src/cosmetic_filter_cache_builder.rs @@ -1,23 +1,19 @@ - use crate::cosmetic_filter_cache::ProceduralOrActionFilter; use crate::cosmetic_filter_utils::SpecificFilterType; -use crate::filters::cosmetic::{ - CosmeticFilter, CosmeticFilterMask, CosmeticFilterOperator}; use crate::cosmetic_filter_utils::{encode_script_with_permission, key_from_selector}; +use crate::filters::cosmetic::{CosmeticFilter, CosmeticFilterMask, CosmeticFilterOperator}; use crate::filters::fb_network::flat::fb; use crate::flatbuffers::containers::flat_map::FlatMapBuilder; use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; use crate::flatbuffers::containers::flat_serialize::{ - serialize_vec_opt, FlatBuilder, FlatSerialize, + serialize_vec_opt, FlatBuilder, FlatSerialize, }; use crate::utils::Hash; use std::collections::{HashMap, HashSet}; - - use flatbuffers::WIPOffset; /// Accumulates hostname-specific rules for a single domain before building HostnameSpecificRules @@ -185,19 +181,31 @@ impl CosmeticFilterCacheBuilder { } // Handle remaining types through HostnameRule Unhide(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + let entry = self + .specific_rules + .entry(*token) + .or_insert_with(HostnameRule::default); entry.unhide.push(s); } UninjectScript((s, _)) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + let entry = self + .specific_rules + .entry(*token) + .or_insert_with(HostnameRule::default); entry.uninject_script.push(s); } ProceduralOrAction(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + let entry = self + .specific_rules + .entry(*token) + .or_insert_with(HostnameRule::default); entry.procedural_action.push(s); } ProceduralOrActionException(s) => { - let entry = self.specific_rules.entry(*token).or_insert_with(HostnameRule::default); + let entry = self + .specific_rules + .entry(*token) + .or_insert_with(HostnameRule::default); entry.procedural_action_exception.push(s); } } diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 7643c9f8..399817ee 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -6,58 +6,57 @@ use memchr::memchr as find_char; /// This should only be called once `selector` has been verified to start with either a "#" or "." /// character. pub(crate) fn key_from_selector(selector: &str) -> Option { - use once_cell::sync::Lazy; - use regex::Regex; + use once_cell::sync::Lazy; + use regex::Regex; - static RE_PLAIN_SELECTOR: Lazy = Lazy::new(|| Regex::new(r"^[#.][\w\\-]+").unwrap()); - static RE_PLAIN_SELECTOR_ESCAPED: Lazy = - Lazy::new(|| Regex::new(r"^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+").unwrap()); - static RE_ESCAPE_SEQUENCE: Lazy = - Lazy::new(|| Regex::new(r"\\([0-9A-Fa-f]+ |.)").unwrap()); + static RE_PLAIN_SELECTOR: Lazy = Lazy::new(|| Regex::new(r"^[#.][\w\\-]+").unwrap()); + static RE_PLAIN_SELECTOR_ESCAPED: Lazy = + Lazy::new(|| Regex::new(r"^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+").unwrap()); + static RE_ESCAPE_SEQUENCE: Lazy = + Lazy::new(|| Regex::new(r"\\([0-9A-Fa-f]+ |.)").unwrap()); - // If there are no escape characters in the selector, just take the first class or id token. - let mat = RE_PLAIN_SELECTOR.find(selector); - if let Some(location) = mat { - let key = &location.as_str(); - if find_char(b'\\', key.as_bytes()).is_none() { - return Some((*key).into()); - } - } else { - return None; - } + // If there are no escape characters in the selector, just take the first class or id token. + let mat = RE_PLAIN_SELECTOR.find(selector); + if let Some(location) = mat { + let key = &location.as_str(); + if find_char(b'\\', key.as_bytes()).is_none() { + return Some((*key).into()); + } + } else { + return None; + } - // Otherwise, the characters in the selector must be escaped. - let mat = RE_PLAIN_SELECTOR_ESCAPED.find(selector); - if let Some(location) = mat { - let mut key = String::with_capacity(selector.len()); - let escaped = &location.as_str(); - let mut beginning = 0; - let mat = RE_ESCAPE_SEQUENCE.captures_iter(escaped); - for capture in mat { - // Unwrap is safe because the 0th capture group is the match itself - let location = capture.get(0).unwrap(); - key += &escaped[beginning..location.start()]; - beginning = location.end(); - // Unwrap is safe because there is a capture group specified in the regex - let capture = capture.get(1).unwrap().as_str(); - if capture.chars().count() == 1 { - // Check number of unicode characters rather than byte length - key += capture; - } else { - // This u32 conversion can overflow - let codepoint = u32::from_str_radix(&capture[..capture.len() - 1], 16).ok()?; + // Otherwise, the characters in the selector must be escaped. + let mat = RE_PLAIN_SELECTOR_ESCAPED.find(selector); + if let Some(location) = mat { + let mut key = String::with_capacity(selector.len()); + let escaped = &location.as_str(); + let mut beginning = 0; + let mat = RE_ESCAPE_SEQUENCE.captures_iter(escaped); + for capture in mat { + // Unwrap is safe because the 0th capture group is the match itself + let location = capture.get(0).unwrap(); + key += &escaped[beginning..location.start()]; + beginning = location.end(); + // Unwrap is safe because there is a capture group specified in the regex + let capture = capture.get(1).unwrap().as_str(); + if capture.chars().count() == 1 { + // Check number of unicode characters rather than byte length + key += capture; + } else { + // This u32 conversion can overflow + let codepoint = u32::from_str_radix(&capture[..capture.len() - 1], 16).ok()?; - // Not all u32s are valid Unicode codepoints - key += &core::char::from_u32(codepoint)?.to_string(); - } - } - Some(key + &escaped[beginning..]) - } else { - None - } + // Not all u32s are valid Unicode codepoints + key += &core::char::from_u32(codepoint)?.to_string(); + } + } + Some(key + &escaped[beginning..]) + } else { + None + } } - /// Exists to use common logic for binning filters correctly #[derive(Clone)] pub(crate) enum SpecificFilterType { @@ -84,21 +83,24 @@ impl SpecificFilterType { /// Encodes permission bits in the last byte of a script string /// Returns the script with permission byte prepended -pub(crate) fn encode_script_with_permission(mut script: String, permission: PermissionMask) -> String { - script.push(permission.to_bits() as char); - script +pub(crate) fn encode_script_with_permission( + mut script: String, + permission: PermissionMask, +) -> String { + script.push(permission.to_bits() as char); + script } /// Decodes permission bits from the last byte of a script string /// Returns (permission, script) tuple pub(crate) fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { - if encoded_script.is_empty() { - return (PermissionMask::default(), encoded_script); - } + if encoded_script.is_empty() { + return (PermissionMask::default(), encoded_script); + } - let last_char = encoded_script.chars().last().unwrap(); - let permission_bits = last_char as u8; - let permission = PermissionMask::from_bits(permission_bits); - let script = &encoded_script[..encoded_script.len() - 1]; - (permission, script) + let last_char = encoded_script.chars().last().unwrap(); + let permission_bits = last_char as u8; + let permission = PermissionMask::from_bits(permission_bits); + let script = &encoded_script[..encoded_script.len() - 1]; + (permission, script) } diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 3850726d..04af17e2 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -15,7 +15,6 @@ use crate::utils::Hash; use super::fb_network::flat::fb; - #[derive(Default)] pub(crate) struct EngineFlatBuilder<'a> { fb_builder: flatbuffers::FlatBufferBuilder<'a>, @@ -46,7 +45,7 @@ impl<'a> EngineFlatBuilder<'a> { self.raw_builder(), &fb::EngineArgs { version, - network_rules: Some(network_rules), + network_rules: Some(network_rules), unique_domains_hashes, cosmetic_filters: Some(cosmetic_rules), }, @@ -66,7 +65,6 @@ impl<'a> FlatBuilder<'a> for EngineFlatBuilder<'a> { } } - pub fn make_flatbuffer( network_filters: Vec, cosmetic_filters: Vec, diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index 43a44d49..2a57724f 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -90,12 +90,7 @@ pub(crate) struct FilterDataContext { impl Default for FilterDataContext { fn default() -> Self { Self { - memory: crate::filters::fb_builder::make_flatbuffer( - vec![], - vec![], - false, - 0, - ), + memory: crate::filters::fb_builder::make_flatbuffer(vec![], vec![], false, 0), unique_domains_hashes_map: HashMap::new(), } } diff --git a/src/filters/fb_network_builder.rs b/src/filters/fb_network_builder.rs index e37d5550..28d37126 100644 --- a/src/filters/fb_network_builder.rs +++ b/src/filters/fb_network_builder.rs @@ -7,7 +7,7 @@ use flatbuffers::WIPOffset; use crate::filters::fb_builder::EngineFlatBuilder; use crate::filters::network::NetworkFilter; -use crate::filters::network::{NetworkFilterMaskHelper}; +use crate::filters::network::NetworkFilterMaskHelper; use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, FlatSerialize, WIPFlatVec}; use crate::network_filter_list::token_histogram; @@ -17,15 +17,15 @@ use crate::utils::{to_short_hash, Hash, ShortHash}; use super::fb_network::flat::fb; pub(crate) enum NetworkFilterListId { - Csp = 0, - Exceptions = 1, - Importants = 2, - Redirects = 3, - RemoveParam = 4, - Filters = 5, - GenericHide = 6, - TaggedFiltersAll = 7, - Size = 8, + Csp = 0, + Exceptions = 1, + Importants = 2, + Redirects = 3, + RemoveParam = 4, + Filters = 5, + GenericHide = 6, + TaggedFiltersAll = 7, + Size = 8, } #[derive(Default, Clone)] @@ -35,257 +35,257 @@ pub(crate) struct NetworkFilterListBuilder { } pub(crate) struct NetworkRulesBuilder { - lists: Vec, + lists: Vec, } impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { - type Output = WIPOffset>; - - fn serialize( - network_filter: &NetworkFilter, - builder: &mut EngineFlatBuilder<'a>, - ) -> WIPOffset> { - let opt_domains = network_filter.opt_domains.as_ref().map(|v| { - let mut o: Vec = v - .iter() - .map(|x| builder.get_or_insert_unique_domain_hash(x)) - .collect(); - o.sort_unstable(); - o.dedup(); - FlatSerialize::serialize(o, builder) - }); - - let opt_not_domains = network_filter.opt_not_domains.as_ref().map(|v| { - let mut o: Vec = v - .iter() - .map(|x| builder.get_or_insert_unique_domain_hash(x)) - .collect(); - o.sort_unstable(); - o.dedup(); - FlatSerialize::serialize(o, builder) - }); - - let modifier_option = network_filter - .modifier_option - .as_ref() - .map(|s| builder.create_string(s)); - - let hostname = network_filter - .hostname - .as_ref() - .map(|s| builder.create_string(s)); - - let tag = network_filter - .tag - .as_ref() - .map(|s| builder.create_string(s)); - - let patterns = if network_filter.filter.iter().len() > 0 { - let offsets: Vec> = network_filter - .filter - .iter() - .map(|s| builder.create_string(s)) - .collect(); - Some(FlatSerialize::serialize(offsets, builder)) - } else { - None - }; - - let raw_line = network_filter - .raw_line - .as_ref() - .map(|v| builder.create_string(v.as_str())); - - let network_filter = fb::NetworkFilter::create( - &mut builder.raw_builder(), - &fb::NetworkFilterArgs { - mask: network_filter.mask.bits(), - patterns, - modifier_option, - opt_domains, - opt_not_domains, - hostname, - tag, - raw_line, - }, - ); - - network_filter - } + type Output = WIPOffset>; + + fn serialize( + network_filter: &NetworkFilter, + builder: &mut EngineFlatBuilder<'a>, + ) -> WIPOffset> { + let opt_domains = network_filter.opt_domains.as_ref().map(|v| { + let mut o: Vec = v + .iter() + .map(|x| builder.get_or_insert_unique_domain_hash(x)) + .collect(); + o.sort_unstable(); + o.dedup(); + FlatSerialize::serialize(o, builder) + }); + + let opt_not_domains = network_filter.opt_not_domains.as_ref().map(|v| { + let mut o: Vec = v + .iter() + .map(|x| builder.get_or_insert_unique_domain_hash(x)) + .collect(); + o.sort_unstable(); + o.dedup(); + FlatSerialize::serialize(o, builder) + }); + + let modifier_option = network_filter + .modifier_option + .as_ref() + .map(|s| builder.create_string(s)); + + let hostname = network_filter + .hostname + .as_ref() + .map(|s| builder.create_string(s)); + + let tag = network_filter + .tag + .as_ref() + .map(|s| builder.create_string(s)); + + let patterns = if network_filter.filter.iter().len() > 0 { + let offsets: Vec> = network_filter + .filter + .iter() + .map(|s| builder.create_string(s)) + .collect(); + Some(FlatSerialize::serialize(offsets, builder)) + } else { + None + }; + + let raw_line = network_filter + .raw_line + .as_ref() + .map(|v| builder.create_string(v.as_str())); + + let network_filter = fb::NetworkFilter::create( + &mut builder.raw_builder(), + &fb::NetworkFilterArgs { + mask: network_filter.mask.bits(), + patterns, + modifier_option, + opt_domains, + opt_not_domains, + hostname, + tag, + raw_line, + }, + ); + + network_filter + } } impl NetworkFilterListBuilder { - fn new(optimize: bool) -> Self { - Self { - filters: vec![], - optimize, - } - } + fn new(optimize: bool) -> Self { + Self { + filters: vec![], + optimize, + } + } } impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkFilterListBuilder { - type Output = WIPOffset>; - fn serialize( - rule_list: Self, - builder: &mut EngineFlatBuilder<'a>, - ) -> WIPOffset> { - let mut filter_map = HashMap::>>>::new(); - - let mut optimizable = HashMap::>::new(); - - // Compute tokens for all filters - let filter_tokens: Vec<_> = rule_list - .filters - .into_iter() - .map(|filter| { - let tokens = filter.get_tokens(); - (filter, tokens) - }) - .collect(); - - // compute the tokens' frequency histogram - let (total_number_of_tokens, tokens_histogram) = token_histogram(&filter_tokens); - - { - for (network_filter, multi_tokens) in filter_tokens.into_iter() { - let flat_filter = if !rule_list.optimize - || !optimizer::is_filter_optimizable_by_patterns(&network_filter) - { - Some(FlatSerialize::serialize(&network_filter, builder)) - } else { - None - }; - - for tokens in multi_tokens { - let mut best_token: ShortHash = 0; - let mut min_count = total_number_of_tokens + 1; - for token in tokens { - let token = to_short_hash(token); - match tokens_histogram.get(&token) { - None => { - min_count = 0; - best_token = token - } - Some(&count) if count < min_count => { - min_count = count; - best_token = token - } - _ => {} - } - } - - if let Some(flat_filter) = flat_filter { - filter_map.entry(best_token).or_default().push(flat_filter); - } else { - optimizable - .entry(best_token) - .or_default() - .push(network_filter.clone()); - } - } // tokens - } - } - - if rule_list.optimize { - // Sort the entries to ensure deterministic iteration order - let mut optimizable_entries: Vec<_> = optimizable.drain().collect(); - optimizable_entries.sort_unstable_by_key(|(token, _)| *token); - - for (token, v) in optimizable_entries { - let optimized = optimizer::optimize(v); - - for filter in optimized { - let flat_filter = FlatSerialize::serialize(&filter, builder); - filter_map.entry(token).or_default().push(flat_filter); - } - } - } else { - debug_assert!( - optimizable.is_empty(), - "Should be empty if optimization is off" - ); - } - - let flat_filter_map_builder = FlatMultiMapBuilder::from_filter_map(filter_map); - let flat_filter_map = FlatMultiMapBuilder::finish(flat_filter_map_builder, builder); - - fb::NetworkFilterList::create( - builder.raw_builder(), - &fb::NetworkFilterListArgs { - filter_map_index: Some(flat_filter_map.keys), - filter_map_values: Some(flat_filter_map.values), - }, - ) - } + type Output = WIPOffset>; + fn serialize( + rule_list: Self, + builder: &mut EngineFlatBuilder<'a>, + ) -> WIPOffset> { + let mut filter_map = HashMap::>>>::new(); + + let mut optimizable = HashMap::>::new(); + + // Compute tokens for all filters + let filter_tokens: Vec<_> = rule_list + .filters + .into_iter() + .map(|filter| { + let tokens = filter.get_tokens(); + (filter, tokens) + }) + .collect(); + + // compute the tokens' frequency histogram + let (total_number_of_tokens, tokens_histogram) = token_histogram(&filter_tokens); + + { + for (network_filter, multi_tokens) in filter_tokens.into_iter() { + let flat_filter = if !rule_list.optimize + || !optimizer::is_filter_optimizable_by_patterns(&network_filter) + { + Some(FlatSerialize::serialize(&network_filter, builder)) + } else { + None + }; + + for tokens in multi_tokens { + let mut best_token: ShortHash = 0; + let mut min_count = total_number_of_tokens + 1; + for token in tokens { + let token = to_short_hash(token); + match tokens_histogram.get(&token) { + None => { + min_count = 0; + best_token = token + } + Some(&count) if count < min_count => { + min_count = count; + best_token = token + } + _ => {} + } + } + + if let Some(flat_filter) = flat_filter { + filter_map.entry(best_token).or_default().push(flat_filter); + } else { + optimizable + .entry(best_token) + .or_default() + .push(network_filter.clone()); + } + } // tokens + } + } + + if rule_list.optimize { + // Sort the entries to ensure deterministic iteration order + let mut optimizable_entries: Vec<_> = optimizable.drain().collect(); + optimizable_entries.sort_unstable_by_key(|(token, _)| *token); + + for (token, v) in optimizable_entries { + let optimized = optimizer::optimize(v); + + for filter in optimized { + let flat_filter = FlatSerialize::serialize(&filter, builder); + filter_map.entry(token).or_default().push(flat_filter); + } + } + } else { + debug_assert!( + optimizable.is_empty(), + "Should be empty if optimization is off" + ); + } + + let flat_filter_map_builder = FlatMultiMapBuilder::from_filter_map(filter_map); + let flat_filter_map = FlatMultiMapBuilder::finish(flat_filter_map_builder, builder); + + fb::NetworkFilterList::create( + builder.raw_builder(), + &fb::NetworkFilterListArgs { + filter_map_index: Some(flat_filter_map.keys), + filter_map_values: Some(flat_filter_map.values), + }, + ) + } } impl NetworkRulesBuilder { - pub fn from_rules(network_filters: Vec, optimize: bool) -> Self { - let mut lists = vec![]; - for list_id in 0..NetworkFilterListId::Size as usize { - // Don't optimize removeparam, since it can fuse filters without respecting distinct - let optimize = optimize && list_id != NetworkFilterListId::RemoveParam as usize; - lists.push(NetworkFilterListBuilder::new(optimize)); - } - let mut self_ = Self { lists }; - - let mut badfilter_ids: HashSet = HashSet::new(); - - // Collect badfilter ids in advance. - for filter in network_filters.iter() { - if filter.is_badfilter() { - badfilter_ids.insert(filter.get_id_without_badfilter()); - } - } - - for filter in network_filters.into_iter() { - // skip any bad filters - let filter_id = filter.get_id(); - if badfilter_ids.contains(&filter_id) || filter.is_badfilter() { - continue; - } - - // Redirects are independent of blocking behavior. - if filter.is_redirect() { - self_.add_filter(filter.clone(), NetworkFilterListId::Redirects); - } - type FilterId = NetworkFilterListId; - - let list_id: FilterId = if filter.is_csp() { - FilterId::Csp - } else if filter.is_removeparam() { - FilterId::RemoveParam - } else if filter.is_generic_hide() { - FilterId::GenericHide - } else if filter.is_exception() { - FilterId::Exceptions - } else if filter.is_important() { - FilterId::Importants - } else if filter.tag.is_some() && !filter.is_redirect() { - // `tag` + `redirect` is unsupported for now. - FilterId::TaggedFiltersAll - } else if (filter.is_redirect() && filter.also_block_redirect()) - || !filter.is_redirect() - { - FilterId::Filters - } else { - continue; - }; - - self_.add_filter(filter, list_id); - } - - self_ - } - - fn add_filter(&mut self, network_filter: NetworkFilter, list_id: NetworkFilterListId) { - self.lists[list_id as usize].filters.push(network_filter); - } + pub fn from_rules(network_filters: Vec, optimize: bool) -> Self { + let mut lists = vec![]; + for list_id in 0..NetworkFilterListId::Size as usize { + // Don't optimize removeparam, since it can fuse filters without respecting distinct + let optimize = optimize && list_id != NetworkFilterListId::RemoveParam as usize; + lists.push(NetworkFilterListBuilder::new(optimize)); + } + let mut self_ = Self { lists }; + + let mut badfilter_ids: HashSet = HashSet::new(); + + // Collect badfilter ids in advance. + for filter in network_filters.iter() { + if filter.is_badfilter() { + badfilter_ids.insert(filter.get_id_without_badfilter()); + } + } + + for filter in network_filters.into_iter() { + // skip any bad filters + let filter_id = filter.get_id(); + if badfilter_ids.contains(&filter_id) || filter.is_badfilter() { + continue; + } + + // Redirects are independent of blocking behavior. + if filter.is_redirect() { + self_.add_filter(filter.clone(), NetworkFilterListId::Redirects); + } + type FilterId = NetworkFilterListId; + + let list_id: FilterId = if filter.is_csp() { + FilterId::Csp + } else if filter.is_removeparam() { + FilterId::RemoveParam + } else if filter.is_generic_hide() { + FilterId::GenericHide + } else if filter.is_exception() { + FilterId::Exceptions + } else if filter.is_important() { + FilterId::Importants + } else if filter.tag.is_some() && !filter.is_redirect() { + // `tag` + `redirect` is unsupported for now. + FilterId::TaggedFiltersAll + } else if (filter.is_redirect() && filter.also_block_redirect()) + || !filter.is_redirect() + { + FilterId::Filters + } else { + continue; + }; + + self_.add_filter(filter, list_id); + } + + self_ + } + + fn add_filter(&mut self, network_filter: NetworkFilter, list_id: NetworkFilterListId) { + self.lists[list_id as usize].filters.push(network_filter); + } } impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder { - type Output = WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>; - fn serialize(value: Self, builder: &mut EngineFlatBuilder<'a>) -> Self::Output { - FlatSerialize::serialize(value.lists, builder) - } + type Output = WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>; + fn serialize(value: Self, builder: &mut EngineFlatBuilder<'a>) -> Self::Output { + FlatSerialize::serialize(value.lists, builder) + } } diff --git a/src/flatbuffers/fb_network_filter_generated.rs b/src/flatbuffers/fb_network_filter_generated.rs index a36d9e8a..d2179b84 100644 --- a/src/flatbuffers/fb_network_filter_generated.rs +++ b/src/flatbuffers/fb_network_filter_generated.rs @@ -1,10 +1,9 @@ // automatically generated by the FlatBuffers compiler, do not modify - // @generated -use core::mem; use core::cmp::Ordering; +use core::mem; extern crate flatbuffers; use self::flatbuffers::{EndianScalar, Follow}; @@ -12,1491 +11,2227 @@ use self::flatbuffers::{EndianScalar, Follow}; #[allow(unused_imports, dead_code)] pub mod fb { - use core::mem; - use core::cmp::Ordering; + use core::cmp::Ordering; + use core::mem; - extern crate flatbuffers; - use self::flatbuffers::{EndianScalar, Follow}; + extern crate flatbuffers; + use self::flatbuffers::{EndianScalar, Follow}; -pub enum NetworkFilterOffset {} -#[derive(Copy, Clone, PartialEq)] + pub enum NetworkFilterOffset {} + #[derive(Copy, Clone, PartialEq)] -pub struct NetworkFilter<'a> { - pub _tab: flatbuffers::Table<'a>, -} - -impl<'a> flatbuffers::Follow<'a> for NetworkFilter<'a> { - type Inner = NetworkFilter<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} + pub struct NetworkFilter<'a> { + pub _tab: flatbuffers::Table<'a>, + } -impl<'a> NetworkFilter<'a> { - pub const VT_MASK: flatbuffers::VOffsetT = 4; - pub const VT_OPT_DOMAINS: flatbuffers::VOffsetT = 6; - pub const VT_OPT_NOT_DOMAINS: flatbuffers::VOffsetT = 8; - pub const VT_PATTERNS: flatbuffers::VOffsetT = 10; - pub const VT_MODIFIER_OPTION: flatbuffers::VOffsetT = 12; - pub const VT_HOSTNAME: flatbuffers::VOffsetT = 14; - pub const VT_TAG: flatbuffers::VOffsetT = 16; - pub const VT_RAW_LINE: flatbuffers::VOffsetT = 18; + impl<'a> flatbuffers::Follow<'a> for NetworkFilter<'a> { + type Inner = NetworkFilter<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - NetworkFilter { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args NetworkFilterArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = NetworkFilterBuilder::new(_fbb); - if let Some(x) = args.raw_line { builder.add_raw_line(x); } - if let Some(x) = args.tag { builder.add_tag(x); } - if let Some(x) = args.hostname { builder.add_hostname(x); } - if let Some(x) = args.modifier_option { builder.add_modifier_option(x); } - if let Some(x) = args.patterns { builder.add_patterns(x); } - if let Some(x) = args.opt_not_domains { builder.add_opt_not_domains(x); } - if let Some(x) = args.opt_domains { builder.add_opt_domains(x); } - builder.add_mask(args.mask); - builder.finish() - } + impl<'a> NetworkFilter<'a> { + pub const VT_MASK: flatbuffers::VOffsetT = 4; + pub const VT_OPT_DOMAINS: flatbuffers::VOffsetT = 6; + pub const VT_OPT_NOT_DOMAINS: flatbuffers::VOffsetT = 8; + pub const VT_PATTERNS: flatbuffers::VOffsetT = 10; + pub const VT_MODIFIER_OPTION: flatbuffers::VOffsetT = 12; + pub const VT_HOSTNAME: flatbuffers::VOffsetT = 14; + pub const VT_TAG: flatbuffers::VOffsetT = 16; + pub const VT_RAW_LINE: flatbuffers::VOffsetT = 18; - pub fn unpack(&self) -> NetworkFilterT { - let mask = self.mask(); - let opt_domains = self.opt_domains().map(|x| { - x.into_iter().collect() - }); - let opt_not_domains = self.opt_not_domains().map(|x| { - x.into_iter().collect() - }); - let patterns = self.patterns().map(|x| { - x.iter().map(|s| s.to_string()).collect() - }); - let modifier_option = self.modifier_option().map(|x| { - x.to_string() - }); - let hostname = self.hostname().map(|x| { - x.to_string() - }); - let tag = self.tag().map(|x| { - x.to_string() - }); - let raw_line = self.raw_line().map(|x| { - x.to_string() - }); - NetworkFilterT { - mask, - opt_domains, - opt_not_domains, - patterns, - modifier_option, - hostname, - tag, - raw_line, - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + NetworkFilter { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NetworkFilterArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = NetworkFilterBuilder::new(_fbb); + if let Some(x) = args.raw_line { + builder.add_raw_line(x); + } + if let Some(x) = args.tag { + builder.add_tag(x); + } + if let Some(x) = args.hostname { + builder.add_hostname(x); + } + if let Some(x) = args.modifier_option { + builder.add_modifier_option(x); + } + if let Some(x) = args.patterns { + builder.add_patterns(x); + } + if let Some(x) = args.opt_not_domains { + builder.add_opt_not_domains(x); + } + if let Some(x) = args.opt_domains { + builder.add_opt_domains(x); + } + builder.add_mask(args.mask); + builder.finish() + } - #[inline] - pub fn mask(&self) -> u32 { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::(NetworkFilter::VT_MASK, Some(0)).unwrap()} - } - #[inline] - pub fn opt_domains(&self) -> Option> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(NetworkFilter::VT_OPT_DOMAINS, None)} - } - #[inline] - pub fn opt_not_domains(&self) -> Option> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(NetworkFilter::VT_OPT_NOT_DOMAINS, None)} - } - #[inline] - pub fn patterns(&self) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(NetworkFilter::VT_PATTERNS, None)} - } - #[inline] - pub fn modifier_option(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>(NetworkFilter::VT_MODIFIER_OPTION, None)} - } - #[inline] - pub fn hostname(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>(NetworkFilter::VT_HOSTNAME, None)} - } - #[inline] - pub fn tag(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>(NetworkFilter::VT_TAG, None)} - } - #[inline] - pub fn raw_line(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>(NetworkFilter::VT_RAW_LINE, None)} - } -} + pub fn unpack(&self) -> NetworkFilterT { + let mask = self.mask(); + let opt_domains = self.opt_domains().map(|x| x.into_iter().collect()); + let opt_not_domains = self.opt_not_domains().map(|x| x.into_iter().collect()); + let patterns = self + .patterns() + .map(|x| x.iter().map(|s| s.to_string()).collect()); + let modifier_option = self.modifier_option().map(|x| x.to_string()); + let hostname = self.hostname().map(|x| x.to_string()); + let tag = self.tag().map(|x| x.to_string()); + let raw_line = self.raw_line().map(|x| x.to_string()); + NetworkFilterT { + mask, + opt_domains, + opt_not_domains, + patterns, + modifier_option, + hostname, + tag, + raw_line, + } + } -impl flatbuffers::Verifiable for NetworkFilter<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("mask", Self::VT_MASK, false)? - .visit_field::>>("opt_domains", Self::VT_OPT_DOMAINS, false)? - .visit_field::>>("opt_not_domains", Self::VT_OPT_NOT_DOMAINS, false)? - .visit_field::>>>("patterns", Self::VT_PATTERNS, false)? - .visit_field::>("modifier_option", Self::VT_MODIFIER_OPTION, false)? - .visit_field::>("hostname", Self::VT_HOSTNAME, false)? - .visit_field::>("tag", Self::VT_TAG, false)? - .visit_field::>("raw_line", Self::VT_RAW_LINE, false)? - .finish(); - Ok(()) - } -} -pub struct NetworkFilterArgs<'a> { - pub mask: u32, - pub opt_domains: Option>>, - pub opt_not_domains: Option>>, - pub patterns: Option>>>, - pub modifier_option: Option>, - pub hostname: Option>, - pub tag: Option>, - pub raw_line: Option>, -} -impl<'a> Default for NetworkFilterArgs<'a> { - #[inline] - fn default() -> Self { - NetworkFilterArgs { - mask: 0, - opt_domains: None, - opt_not_domains: None, - patterns: None, - modifier_option: None, - hostname: None, - tag: None, - raw_line: None, - } - } -} + #[inline] + pub fn mask(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(NetworkFilter::VT_MASK, Some(0)) + .unwrap() + } + } + #[inline] + pub fn opt_domains(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + NetworkFilter::VT_OPT_DOMAINS, + None, + ) + } + } + #[inline] + pub fn opt_not_domains(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + NetworkFilter::VT_OPT_NOT_DOMAINS, + None, + ) + } + } + #[inline] + pub fn patterns( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(NetworkFilter::VT_PATTERNS, None) + } + } + #[inline] + pub fn modifier_option(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>( + NetworkFilter::VT_MODIFIER_OPTION, + None, + ) + } + } + #[inline] + pub fn hostname(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(NetworkFilter::VT_HOSTNAME, None) + } + } + #[inline] + pub fn tag(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(NetworkFilter::VT_TAG, None) + } + } + #[inline] + pub fn raw_line(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(NetworkFilter::VT_RAW_LINE, None) + } + } + } -pub struct NetworkFilterBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterBuilder<'a, 'b, A> { - #[inline] - pub fn add_mask(&mut self, mask: u32) { - self.fbb_.push_slot::(NetworkFilter::VT_MASK, mask, 0); - } - #[inline] - pub fn add_opt_domains(&mut self, opt_domains: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_OPT_DOMAINS, opt_domains); - } - #[inline] - pub fn add_opt_not_domains(&mut self, opt_not_domains: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_OPT_NOT_DOMAINS, opt_not_domains); - } - #[inline] - pub fn add_patterns(&mut self, patterns: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_PATTERNS, patterns); - } - #[inline] - pub fn add_modifier_option(&mut self, modifier_option: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_MODIFIER_OPTION, modifier_option); - } - #[inline] - pub fn add_hostname(&mut self, hostname: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_HOSTNAME, hostname); - } - #[inline] - pub fn add_tag(&mut self, tag: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_TAG, tag); - } - #[inline] - pub fn add_raw_line(&mut self, raw_line: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(NetworkFilter::VT_RAW_LINE, raw_line); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> NetworkFilterBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - NetworkFilterBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } -} + impl flatbuffers::Verifiable for NetworkFilter<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("mask", Self::VT_MASK, false)? + .visit_field::>>( + "opt_domains", + Self::VT_OPT_DOMAINS, + false, + )? + .visit_field::>>( + "opt_not_domains", + Self::VT_OPT_NOT_DOMAINS, + false, + )? + .visit_field::>, + >>("patterns", Self::VT_PATTERNS, false)? + .visit_field::>( + "modifier_option", + Self::VT_MODIFIER_OPTION, + false, + )? + .visit_field::>( + "hostname", + Self::VT_HOSTNAME, + false, + )? + .visit_field::>("tag", Self::VT_TAG, false)? + .visit_field::>( + "raw_line", + Self::VT_RAW_LINE, + false, + )? + .finish(); + Ok(()) + } + } + pub struct NetworkFilterArgs<'a> { + pub mask: u32, + pub opt_domains: Option>>, + pub opt_not_domains: Option>>, + pub patterns: Option< + flatbuffers::WIPOffset>>, + >, + pub modifier_option: Option>, + pub hostname: Option>, + pub tag: Option>, + pub raw_line: Option>, + } + impl<'a> Default for NetworkFilterArgs<'a> { + #[inline] + fn default() -> Self { + NetworkFilterArgs { + mask: 0, + opt_domains: None, + opt_not_domains: None, + patterns: None, + modifier_option: None, + hostname: None, + tag: None, + raw_line: None, + } + } + } -impl core::fmt::Debug for NetworkFilter<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("NetworkFilter"); - ds.field("mask", &self.mask()); - ds.field("opt_domains", &self.opt_domains()); - ds.field("opt_not_domains", &self.opt_not_domains()); - ds.field("patterns", &self.patterns()); - ds.field("modifier_option", &self.modifier_option()); - ds.field("hostname", &self.hostname()); - ds.field("tag", &self.tag()); - ds.field("raw_line", &self.raw_line()); - ds.finish() - } -} -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub struct NetworkFilterT { - pub mask: u32, - pub opt_domains: Option>, - pub opt_not_domains: Option>, - pub patterns: Option>, - pub modifier_option: Option, - pub hostname: Option, - pub tag: Option, - pub raw_line: Option, -} -impl Default for NetworkFilterT { - fn default() -> Self { - Self { - mask: 0, - opt_domains: None, - opt_not_domains: None, - patterns: None, - modifier_option: None, - hostname: None, - tag: None, - raw_line: None, - } - } -} -impl NetworkFilterT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> - ) -> flatbuffers::WIPOffset> { - let mask = self.mask; - let opt_domains = self.opt_domains.as_ref().map(|x|{ - _fbb.create_vector(x) - }); - let opt_not_domains = self.opt_not_domains.as_ref().map(|x|{ - _fbb.create_vector(x) - }); - let patterns = self.patterns.as_ref().map(|x|{ - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let modifier_option = self.modifier_option.as_ref().map(|x|{ - _fbb.create_string(x) - }); - let hostname = self.hostname.as_ref().map(|x|{ - _fbb.create_string(x) - }); - let tag = self.tag.as_ref().map(|x|{ - _fbb.create_string(x) - }); - let raw_line = self.raw_line.as_ref().map(|x|{ - _fbb.create_string(x) - }); - NetworkFilter::create(_fbb, &NetworkFilterArgs{ - mask, - opt_domains, - opt_not_domains, - patterns, - modifier_option, - hostname, - tag, - raw_line, - }) - } -} -pub enum NetworkFilterListOffset {} -#[derive(Copy, Clone, PartialEq)] + pub struct NetworkFilterBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterBuilder<'a, 'b, A> { + #[inline] + pub fn add_mask(&mut self, mask: u32) { + self.fbb_.push_slot::(NetworkFilter::VT_MASK, mask, 0); + } + #[inline] + pub fn add_opt_domains( + &mut self, + opt_domains: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_OPT_DOMAINS, + opt_domains, + ); + } + #[inline] + pub fn add_opt_not_domains( + &mut self, + opt_not_domains: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_OPT_NOT_DOMAINS, + opt_not_domains, + ); + } + #[inline] + pub fn add_patterns( + &mut self, + patterns: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_PATTERNS, + patterns, + ); + } + #[inline] + pub fn add_modifier_option(&mut self, modifier_option: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_MODIFIER_OPTION, + modifier_option, + ); + } + #[inline] + pub fn add_hostname(&mut self, hostname: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_HOSTNAME, + hostname, + ); + } + #[inline] + pub fn add_tag(&mut self, tag: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(NetworkFilter::VT_TAG, tag); + } + #[inline] + pub fn add_raw_line(&mut self, raw_line: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + NetworkFilter::VT_RAW_LINE, + raw_line, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> NetworkFilterBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NetworkFilterBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } -pub struct NetworkFilterList<'a> { - pub _tab: flatbuffers::Table<'a>, -} + impl core::fmt::Debug for NetworkFilter<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("NetworkFilter"); + ds.field("mask", &self.mask()); + ds.field("opt_domains", &self.opt_domains()); + ds.field("opt_not_domains", &self.opt_not_domains()); + ds.field("patterns", &self.patterns()); + ds.field("modifier_option", &self.modifier_option()); + ds.field("hostname", &self.hostname()); + ds.field("tag", &self.tag()); + ds.field("raw_line", &self.raw_line()); + ds.finish() + } + } + #[non_exhaustive] + #[derive(Debug, Clone, PartialEq)] + pub struct NetworkFilterT { + pub mask: u32, + pub opt_domains: Option>, + pub opt_not_domains: Option>, + pub patterns: Option>, + pub modifier_option: Option, + pub hostname: Option, + pub tag: Option, + pub raw_line: Option, + } + impl Default for NetworkFilterT { + fn default() -> Self { + Self { + mask: 0, + opt_domains: None, + opt_not_domains: None, + patterns: None, + modifier_option: None, + hostname: None, + tag: None, + raw_line: None, + } + } + } + impl NetworkFilterT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, + ) -> flatbuffers::WIPOffset> { + let mask = self.mask; + let opt_domains = self.opt_domains.as_ref().map(|x| _fbb.create_vector(x)); + let opt_not_domains = self.opt_not_domains.as_ref().map(|x| _fbb.create_vector(x)); + let patterns = self.patterns.as_ref().map(|x| { + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let modifier_option = self.modifier_option.as_ref().map(|x| _fbb.create_string(x)); + let hostname = self.hostname.as_ref().map(|x| _fbb.create_string(x)); + let tag = self.tag.as_ref().map(|x| _fbb.create_string(x)); + let raw_line = self.raw_line.as_ref().map(|x| _fbb.create_string(x)); + NetworkFilter::create( + _fbb, + &NetworkFilterArgs { + mask, + opt_domains, + opt_not_domains, + patterns, + modifier_option, + hostname, + tag, + raw_line, + }, + ) + } + } + pub enum NetworkFilterListOffset {} + #[derive(Copy, Clone, PartialEq)] -impl<'a> flatbuffers::Follow<'a> for NetworkFilterList<'a> { - type Inner = NetworkFilterList<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} + pub struct NetworkFilterList<'a> { + pub _tab: flatbuffers::Table<'a>, + } -impl<'a> NetworkFilterList<'a> { - pub const VT_FILTER_MAP_INDEX: flatbuffers::VOffsetT = 4; - pub const VT_FILTER_MAP_VALUES: flatbuffers::VOffsetT = 6; + impl<'a> flatbuffers::Follow<'a> for NetworkFilterList<'a> { + type Inner = NetworkFilterList<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - NetworkFilterList { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args NetworkFilterListArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = NetworkFilterListBuilder::new(_fbb); - if let Some(x) = args.filter_map_values { builder.add_filter_map_values(x); } - if let Some(x) = args.filter_map_index { builder.add_filter_map_index(x); } - builder.finish() - } + impl<'a> NetworkFilterList<'a> { + pub const VT_FILTER_MAP_INDEX: flatbuffers::VOffsetT = 4; + pub const VT_FILTER_MAP_VALUES: flatbuffers::VOffsetT = 6; - pub fn unpack(&self) -> NetworkFilterListT { - let filter_map_index = { - let x = self.filter_map_index(); - x.into_iter().collect() - }; - let filter_map_values = { - let x = self.filter_map_values(); - x.iter().map(|t| t.unpack()).collect() - }; - NetworkFilterListT { - filter_map_index, - filter_map_values, - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + NetworkFilterList { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args NetworkFilterListArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = NetworkFilterListBuilder::new(_fbb); + if let Some(x) = args.filter_map_values { + builder.add_filter_map_values(x); + } + if let Some(x) = args.filter_map_index { + builder.add_filter_map_index(x); + } + builder.finish() + } - #[inline] - pub fn filter_map_index(&self) -> flatbuffers::Vector<'a, u32> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(NetworkFilterList::VT_FILTER_MAP_INDEX, None).unwrap()} - } - #[inline] - pub fn filter_map_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(NetworkFilterList::VT_FILTER_MAP_VALUES, None).unwrap()} - } -} + pub fn unpack(&self) -> NetworkFilterListT { + let filter_map_index = { + let x = self.filter_map_index(); + x.into_iter().collect() + }; + let filter_map_values = { + let x = self.filter_map_values(); + x.iter().map(|t| t.unpack()).collect() + }; + NetworkFilterListT { + filter_map_index, + filter_map_values, + } + } -impl flatbuffers::Verifiable for NetworkFilterList<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>("filter_map_index", Self::VT_FILTER_MAP_INDEX, true)? - .visit_field::>>>("filter_map_values", Self::VT_FILTER_MAP_VALUES, true)? - .finish(); - Ok(()) - } -} -pub struct NetworkFilterListArgs<'a> { - pub filter_map_index: Option>>, - pub filter_map_values: Option>>>>, -} -impl<'a> Default for NetworkFilterListArgs<'a> { - #[inline] - fn default() -> Self { - NetworkFilterListArgs { - filter_map_index: None, // required field - filter_map_values: None, // required field - } - } -} + #[inline] + pub fn filter_map_index(&self) -> flatbuffers::Vector<'a, u32> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + NetworkFilterList::VT_FILTER_MAP_INDEX, + None, + ) + .unwrap() + } + } + #[inline] + pub fn filter_map_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(NetworkFilterList::VT_FILTER_MAP_VALUES, None) + .unwrap() + } + } + } -pub struct NetworkFilterListBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterListBuilder<'a, 'b, A> { - #[inline] - pub fn add_filter_map_index(&mut self, filter_map_index: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(NetworkFilterList::VT_FILTER_MAP_INDEX, filter_map_index); - } - #[inline] - pub fn add_filter_map_values(&mut self, filter_map_values: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(NetworkFilterList::VT_FILTER_MAP_VALUES, filter_map_values); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> NetworkFilterListBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - NetworkFilterListBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, NetworkFilterList::VT_FILTER_MAP_INDEX,"filter_map_index"); - self.fbb_.required(o, NetworkFilterList::VT_FILTER_MAP_VALUES,"filter_map_values"); - flatbuffers::WIPOffset::new(o.value()) - } -} + impl flatbuffers::Verifiable for NetworkFilterList<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>>( + "filter_map_index", + Self::VT_FILTER_MAP_INDEX, + true, + )? + .visit_field::>, + >>("filter_map_values", Self::VT_FILTER_MAP_VALUES, true)? + .finish(); + Ok(()) + } + } + pub struct NetworkFilterListArgs<'a> { + pub filter_map_index: Option>>, + pub filter_map_values: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + } + impl<'a> Default for NetworkFilterListArgs<'a> { + #[inline] + fn default() -> Self { + NetworkFilterListArgs { + filter_map_index: None, // required field + filter_map_values: None, // required field + } + } + } -impl core::fmt::Debug for NetworkFilterList<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("NetworkFilterList"); - ds.field("filter_map_index", &self.filter_map_index()); - ds.field("filter_map_values", &self.filter_map_values()); - ds.finish() - } -} -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub struct NetworkFilterListT { - pub filter_map_index: Vec, - pub filter_map_values: Vec, -} -impl Default for NetworkFilterListT { - fn default() -> Self { - Self { - filter_map_index: Default::default(), - filter_map_values: Default::default(), - } - } -} -impl NetworkFilterListT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> - ) -> flatbuffers::WIPOffset> { - let filter_map_index = Some({ - let x = &self.filter_map_index; - _fbb.create_vector(x) - }); - let filter_map_values = Some({ - let x = &self.filter_map_values; - let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) - }); - NetworkFilterList::create(_fbb, &NetworkFilterListArgs{ - filter_map_index, - filter_map_values, - }) - } -} -pub enum HostnameSpecificRulesOffset {} -#[derive(Copy, Clone, PartialEq)] + pub struct NetworkFilterListBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> NetworkFilterListBuilder<'a, 'b, A> { + #[inline] + pub fn add_filter_map_index( + &mut self, + filter_map_index: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + NetworkFilterList::VT_FILTER_MAP_INDEX, + filter_map_index, + ); + } + #[inline] + pub fn add_filter_map_values( + &mut self, + filter_map_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_.push_slot_always::>( + NetworkFilterList::VT_FILTER_MAP_VALUES, + filter_map_values, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> NetworkFilterListBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NetworkFilterListBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required( + o, + NetworkFilterList::VT_FILTER_MAP_INDEX, + "filter_map_index", + ); + self.fbb_.required( + o, + NetworkFilterList::VT_FILTER_MAP_VALUES, + "filter_map_values", + ); + flatbuffers::WIPOffset::new(o.value()) + } + } -pub struct HostnameSpecificRules<'a> { - pub _tab: flatbuffers::Table<'a>, -} + impl core::fmt::Debug for NetworkFilterList<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("NetworkFilterList"); + ds.field("filter_map_index", &self.filter_map_index()); + ds.field("filter_map_values", &self.filter_map_values()); + ds.finish() + } + } + #[non_exhaustive] + #[derive(Debug, Clone, PartialEq)] + pub struct NetworkFilterListT { + pub filter_map_index: Vec, + pub filter_map_values: Vec, + } + impl Default for NetworkFilterListT { + fn default() -> Self { + Self { + filter_map_index: Default::default(), + filter_map_values: Default::default(), + } + } + } + impl NetworkFilterListT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, + ) -> flatbuffers::WIPOffset> { + let filter_map_index = Some({ + let x = &self.filter_map_index; + _fbb.create_vector(x) + }); + let filter_map_values = Some({ + let x = &self.filter_map_values; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); + _fbb.create_vector(&w) + }); + NetworkFilterList::create( + _fbb, + &NetworkFilterListArgs { + filter_map_index, + filter_map_values, + }, + ) + } + } + pub enum HostnameSpecificRulesOffset {} + #[derive(Copy, Clone, PartialEq)] -impl<'a> flatbuffers::Follow<'a> for HostnameSpecificRules<'a> { - type Inner = HostnameSpecificRules<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} + pub struct HostnameSpecificRules<'a> { + pub _tab: flatbuffers::Table<'a>, + } -impl<'a> HostnameSpecificRules<'a> { - pub const VT_UNHIDE: flatbuffers::VOffsetT = 4; - pub const VT_UNINJECT_SCRIPT: flatbuffers::VOffsetT = 6; - pub const VT_PROCEDURAL_ACTION: flatbuffers::VOffsetT = 8; - pub const VT_PROCEDURAL_ACTION_EXCEPTION: flatbuffers::VOffsetT = 10; + impl<'a> flatbuffers::Follow<'a> for HostnameSpecificRules<'a> { + type Inner = HostnameSpecificRules<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - HostnameSpecificRules { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args HostnameSpecificRulesArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = HostnameSpecificRulesBuilder::new(_fbb); - if let Some(x) = args.procedural_action_exception { builder.add_procedural_action_exception(x); } - if let Some(x) = args.procedural_action { builder.add_procedural_action(x); } - if let Some(x) = args.uninject_script { builder.add_uninject_script(x); } - if let Some(x) = args.unhide { builder.add_unhide(x); } - builder.finish() - } + impl<'a> HostnameSpecificRules<'a> { + pub const VT_UNHIDE: flatbuffers::VOffsetT = 4; + pub const VT_UNINJECT_SCRIPT: flatbuffers::VOffsetT = 6; + pub const VT_PROCEDURAL_ACTION: flatbuffers::VOffsetT = 8; + pub const VT_PROCEDURAL_ACTION_EXCEPTION: flatbuffers::VOffsetT = 10; - pub fn unpack(&self) -> HostnameSpecificRulesT { - let unhide = self.unhide().map(|x| { - x.iter().map(|s| s.to_string()).collect() - }); - let uninject_script = self.uninject_script().map(|x| { - x.iter().map(|s| s.to_string()).collect() - }); - let procedural_action = self.procedural_action().map(|x| { - x.iter().map(|s| s.to_string()).collect() - }); - let procedural_action_exception = self.procedural_action_exception().map(|x| { - x.iter().map(|s| s.to_string()).collect() - }); - HostnameSpecificRulesT { - unhide, - uninject_script, - procedural_action, - procedural_action_exception, - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + HostnameSpecificRules { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args HostnameSpecificRulesArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = HostnameSpecificRulesBuilder::new(_fbb); + if let Some(x) = args.procedural_action_exception { + builder.add_procedural_action_exception(x); + } + if let Some(x) = args.procedural_action { + builder.add_procedural_action(x); + } + if let Some(x) = args.uninject_script { + builder.add_uninject_script(x); + } + if let Some(x) = args.unhide { + builder.add_unhide(x); + } + builder.finish() + } - #[inline] - pub fn unhide(&self) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_UNHIDE, None)} - } - #[inline] - pub fn uninject_script(&self) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, None)} - } - #[inline] - pub fn procedural_action(&self) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, None)} - } - #[inline] - pub fn procedural_action_exception(&self) -> Option>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, None)} - } -} + pub fn unpack(&self) -> HostnameSpecificRulesT { + let unhide = self + .unhide() + .map(|x| x.iter().map(|s| s.to_string()).collect()); + let uninject_script = self + .uninject_script() + .map(|x| x.iter().map(|s| s.to_string()).collect()); + let procedural_action = self + .procedural_action() + .map(|x| x.iter().map(|s| s.to_string()).collect()); + let procedural_action_exception = self + .procedural_action_exception() + .map(|x| x.iter().map(|s| s.to_string()).collect()); + HostnameSpecificRulesT { + unhide, + uninject_script, + procedural_action, + procedural_action_exception, + } + } -impl flatbuffers::Verifiable for HostnameSpecificRules<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>>("unhide", Self::VT_UNHIDE, false)? - .visit_field::>>>("uninject_script", Self::VT_UNINJECT_SCRIPT, false)? - .visit_field::>>>("procedural_action", Self::VT_PROCEDURAL_ACTION, false)? - .visit_field::>>>("procedural_action_exception", Self::VT_PROCEDURAL_ACTION_EXCEPTION, false)? - .finish(); - Ok(()) - } -} -pub struct HostnameSpecificRulesArgs<'a> { - pub unhide: Option>>>, - pub uninject_script: Option>>>, - pub procedural_action: Option>>>, - pub procedural_action_exception: Option>>>, -} -impl<'a> Default for HostnameSpecificRulesArgs<'a> { - #[inline] - fn default() -> Self { - HostnameSpecificRulesArgs { - unhide: None, - uninject_script: None, - procedural_action: None, - procedural_action_exception: None, - } - } -} + #[inline] + pub fn unhide( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(HostnameSpecificRules::VT_UNHIDE, None) + } + } + #[inline] + pub fn uninject_script( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, None) + } + } + #[inline] + pub fn procedural_action( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, None) + } + } + #[inline] + pub fn procedural_action_exception( + &self, + ) -> Option>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>( + HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, None + ) + } + } + } -pub struct HostnameSpecificRulesBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> HostnameSpecificRulesBuilder<'a, 'b, A> { - #[inline] - pub fn add_unhide(&mut self, unhide: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_UNHIDE, unhide); - } - #[inline] - pub fn add_uninject_script(&mut self, uninject_script: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, uninject_script); - } - #[inline] - pub fn add_procedural_action(&mut self, procedural_action: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, procedural_action); - } - #[inline] - pub fn add_procedural_action_exception(&mut self, procedural_action_exception: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, procedural_action_exception); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> HostnameSpecificRulesBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - HostnameSpecificRulesBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } -} + impl flatbuffers::Verifiable for HostnameSpecificRules<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>, + >>("unhide", Self::VT_UNHIDE, false)? + .visit_field::>, + >>("uninject_script", Self::VT_UNINJECT_SCRIPT, false)? + .visit_field::>, + >>("procedural_action", Self::VT_PROCEDURAL_ACTION, false)? + .visit_field::>, + >>( + "procedural_action_exception", + Self::VT_PROCEDURAL_ACTION_EXCEPTION, + false, + )? + .finish(); + Ok(()) + } + } + pub struct HostnameSpecificRulesArgs<'a> { + pub unhide: Option< + flatbuffers::WIPOffset>>, + >, + pub uninject_script: Option< + flatbuffers::WIPOffset>>, + >, + pub procedural_action: Option< + flatbuffers::WIPOffset>>, + >, + pub procedural_action_exception: Option< + flatbuffers::WIPOffset>>, + >, + } + impl<'a> Default for HostnameSpecificRulesArgs<'a> { + #[inline] + fn default() -> Self { + HostnameSpecificRulesArgs { + unhide: None, + uninject_script: None, + procedural_action: None, + procedural_action_exception: None, + } + } + } -impl core::fmt::Debug for HostnameSpecificRules<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("HostnameSpecificRules"); - ds.field("unhide", &self.unhide()); - ds.field("uninject_script", &self.uninject_script()); - ds.field("procedural_action", &self.procedural_action()); - ds.field("procedural_action_exception", &self.procedural_action_exception()); - ds.finish() - } -} -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub struct HostnameSpecificRulesT { - pub unhide: Option>, - pub uninject_script: Option>, - pub procedural_action: Option>, - pub procedural_action_exception: Option>, -} -impl Default for HostnameSpecificRulesT { - fn default() -> Self { - Self { - unhide: None, - uninject_script: None, - procedural_action: None, - procedural_action_exception: None, - } - } -} -impl HostnameSpecificRulesT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> - ) -> flatbuffers::WIPOffset> { - let unhide = self.unhide.as_ref().map(|x|{ - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let uninject_script = self.uninject_script.as_ref().map(|x|{ - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let procedural_action = self.procedural_action.as_ref().map(|x|{ - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let procedural_action_exception = self.procedural_action_exception.as_ref().map(|x|{ - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - HostnameSpecificRules::create(_fbb, &HostnameSpecificRulesArgs{ - unhide, - uninject_script, - procedural_action, - procedural_action_exception, - }) - } -} -pub enum CosmeticFiltersOffset {} -#[derive(Copy, Clone, PartialEq)] + pub struct HostnameSpecificRulesBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> HostnameSpecificRulesBuilder<'a, 'b, A> { + #[inline] + pub fn add_unhide( + &mut self, + unhide: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + HostnameSpecificRules::VT_UNHIDE, + unhide, + ); + } + #[inline] + pub fn add_uninject_script( + &mut self, + uninject_script: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + HostnameSpecificRules::VT_UNINJECT_SCRIPT, + uninject_script, + ); + } + #[inline] + pub fn add_procedural_action( + &mut self, + procedural_action: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + HostnameSpecificRules::VT_PROCEDURAL_ACTION, + procedural_action, + ); + } + #[inline] + pub fn add_procedural_action_exception( + &mut self, + procedural_action_exception: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + HostnameSpecificRules::VT_PROCEDURAL_ACTION_EXCEPTION, + procedural_action_exception, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> HostnameSpecificRulesBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + HostnameSpecificRulesBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } + } -pub struct CosmeticFilters<'a> { - pub _tab: flatbuffers::Table<'a>, -} + impl core::fmt::Debug for HostnameSpecificRules<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("HostnameSpecificRules"); + ds.field("unhide", &self.unhide()); + ds.field("uninject_script", &self.uninject_script()); + ds.field("procedural_action", &self.procedural_action()); + ds.field( + "procedural_action_exception", + &self.procedural_action_exception(), + ); + ds.finish() + } + } + #[non_exhaustive] + #[derive(Debug, Clone, PartialEq)] + pub struct HostnameSpecificRulesT { + pub unhide: Option>, + pub uninject_script: Option>, + pub procedural_action: Option>, + pub procedural_action_exception: Option>, + } + impl Default for HostnameSpecificRulesT { + fn default() -> Self { + Self { + unhide: None, + uninject_script: None, + procedural_action: None, + procedural_action_exception: None, + } + } + } + impl HostnameSpecificRulesT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, + ) -> flatbuffers::WIPOffset> { + let unhide = self.unhide.as_ref().map(|x| { + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let uninject_script = self.uninject_script.as_ref().map(|x| { + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let procedural_action = self.procedural_action.as_ref().map(|x| { + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let procedural_action_exception = self.procedural_action_exception.as_ref().map(|x| { + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + HostnameSpecificRules::create( + _fbb, + &HostnameSpecificRulesArgs { + unhide, + uninject_script, + procedural_action, + procedural_action_exception, + }, + ) + } + } + pub enum CosmeticFiltersOffset {} + #[derive(Copy, Clone, PartialEq)] -impl<'a> flatbuffers::Follow<'a> for CosmeticFilters<'a> { - type Inner = CosmeticFilters<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} + pub struct CosmeticFilters<'a> { + pub _tab: flatbuffers::Table<'a>, + } -impl<'a> CosmeticFilters<'a> { - pub const VT_SIMPLE_CLASS_RULES: flatbuffers::VOffsetT = 4; - pub const VT_SIMPLE_ID_RULES: flatbuffers::VOffsetT = 6; - pub const VT_MISC_GENERIC_SELECTORS: flatbuffers::VOffsetT = 8; - pub const VT_COMPLEX_CLASS_RULES_INDEX: flatbuffers::VOffsetT = 10; - pub const VT_COMPLEX_CLASS_RULES_VALUES: flatbuffers::VOffsetT = 12; - pub const VT_COMPLEX_ID_RULES_INDEX: flatbuffers::VOffsetT = 14; - pub const VT_COMPLEX_ID_RULES_VALUES: flatbuffers::VOffsetT = 16; - pub const VT_HOSTNAME_HIDE_INDEX: flatbuffers::VOffsetT = 18; - pub const VT_HOSTNAME_HIDE_VALUES: flatbuffers::VOffsetT = 20; - pub const VT_HOSTNAME_INJECT_SCRIPT_INDEX: flatbuffers::VOffsetT = 22; - pub const VT_HOSTNAME_INJECT_SCRIPT_VALUES: flatbuffers::VOffsetT = 24; - pub const VT_HOSTNAME_INDEX: flatbuffers::VOffsetT = 26; - pub const VT_HOSTNAME_VALUES: flatbuffers::VOffsetT = 28; + impl<'a> flatbuffers::Follow<'a> for CosmeticFilters<'a> { + type Inner = CosmeticFilters<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - CosmeticFilters { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args CosmeticFiltersArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = CosmeticFiltersBuilder::new(_fbb); - if let Some(x) = args.hostname_values { builder.add_hostname_values(x); } - if let Some(x) = args.hostname_index { builder.add_hostname_index(x); } - if let Some(x) = args.hostname_inject_script_values { builder.add_hostname_inject_script_values(x); } - if let Some(x) = args.hostname_inject_script_index { builder.add_hostname_inject_script_index(x); } - if let Some(x) = args.hostname_hide_values { builder.add_hostname_hide_values(x); } - if let Some(x) = args.hostname_hide_index { builder.add_hostname_hide_index(x); } - if let Some(x) = args.complex_id_rules_values { builder.add_complex_id_rules_values(x); } - if let Some(x) = args.complex_id_rules_index { builder.add_complex_id_rules_index(x); } - if let Some(x) = args.complex_class_rules_values { builder.add_complex_class_rules_values(x); } - if let Some(x) = args.complex_class_rules_index { builder.add_complex_class_rules_index(x); } - if let Some(x) = args.misc_generic_selectors { builder.add_misc_generic_selectors(x); } - if let Some(x) = args.simple_id_rules { builder.add_simple_id_rules(x); } - if let Some(x) = args.simple_class_rules { builder.add_simple_class_rules(x); } - builder.finish() - } + impl<'a> CosmeticFilters<'a> { + pub const VT_SIMPLE_CLASS_RULES: flatbuffers::VOffsetT = 4; + pub const VT_SIMPLE_ID_RULES: flatbuffers::VOffsetT = 6; + pub const VT_MISC_GENERIC_SELECTORS: flatbuffers::VOffsetT = 8; + pub const VT_COMPLEX_CLASS_RULES_INDEX: flatbuffers::VOffsetT = 10; + pub const VT_COMPLEX_CLASS_RULES_VALUES: flatbuffers::VOffsetT = 12; + pub const VT_COMPLEX_ID_RULES_INDEX: flatbuffers::VOffsetT = 14; + pub const VT_COMPLEX_ID_RULES_VALUES: flatbuffers::VOffsetT = 16; + pub const VT_HOSTNAME_HIDE_INDEX: flatbuffers::VOffsetT = 18; + pub const VT_HOSTNAME_HIDE_VALUES: flatbuffers::VOffsetT = 20; + pub const VT_HOSTNAME_INJECT_SCRIPT_INDEX: flatbuffers::VOffsetT = 22; + pub const VT_HOSTNAME_INJECT_SCRIPT_VALUES: flatbuffers::VOffsetT = 24; + pub const VT_HOSTNAME_INDEX: flatbuffers::VOffsetT = 26; + pub const VT_HOSTNAME_VALUES: flatbuffers::VOffsetT = 28; - pub fn unpack(&self) -> CosmeticFiltersT { - let simple_class_rules = { - let x = self.simple_class_rules(); - x.iter().map(|s| s.to_string()).collect() - }; - let simple_id_rules = { - let x = self.simple_id_rules(); - x.iter().map(|s| s.to_string()).collect() - }; - let misc_generic_selectors = { - let x = self.misc_generic_selectors(); - x.iter().map(|s| s.to_string()).collect() - }; - let complex_class_rules_index = { - let x = self.complex_class_rules_index(); - x.iter().map(|s| s.to_string()).collect() - }; - let complex_class_rules_values = { - let x = self.complex_class_rules_values(); - x.iter().map(|s| s.to_string()).collect() - }; - let complex_id_rules_index = { - let x = self.complex_id_rules_index(); - x.iter().map(|s| s.to_string()).collect() - }; - let complex_id_rules_values = { - let x = self.complex_id_rules_values(); - x.iter().map(|s| s.to_string()).collect() - }; - let hostname_hide_index = { - let x = self.hostname_hide_index(); - x.into_iter().collect() - }; - let hostname_hide_values = { - let x = self.hostname_hide_values(); - x.iter().map(|s| s.to_string()).collect() - }; - let hostname_inject_script_index = { - let x = self.hostname_inject_script_index(); - x.into_iter().collect() - }; - let hostname_inject_script_values = { - let x = self.hostname_inject_script_values(); - x.iter().map(|s| s.to_string()).collect() - }; - let hostname_index = { - let x = self.hostname_index(); - x.into_iter().collect() - }; - let hostname_values = { - let x = self.hostname_values(); - x.iter().map(|t| t.unpack()).collect() - }; - CosmeticFiltersT { - simple_class_rules, - simple_id_rules, - misc_generic_selectors, - complex_class_rules_index, - complex_class_rules_values, - complex_id_rules_index, - complex_id_rules_values, - hostname_hide_index, - hostname_hide_values, - hostname_inject_script_index, - hostname_inject_script_values, - hostname_index, - hostname_values, - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + CosmeticFilters { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args CosmeticFiltersArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = CosmeticFiltersBuilder::new(_fbb); + if let Some(x) = args.hostname_values { + builder.add_hostname_values(x); + } + if let Some(x) = args.hostname_index { + builder.add_hostname_index(x); + } + if let Some(x) = args.hostname_inject_script_values { + builder.add_hostname_inject_script_values(x); + } + if let Some(x) = args.hostname_inject_script_index { + builder.add_hostname_inject_script_index(x); + } + if let Some(x) = args.hostname_hide_values { + builder.add_hostname_hide_values(x); + } + if let Some(x) = args.hostname_hide_index { + builder.add_hostname_hide_index(x); + } + if let Some(x) = args.complex_id_rules_values { + builder.add_complex_id_rules_values(x); + } + if let Some(x) = args.complex_id_rules_index { + builder.add_complex_id_rules_index(x); + } + if let Some(x) = args.complex_class_rules_values { + builder.add_complex_class_rules_values(x); + } + if let Some(x) = args.complex_class_rules_index { + builder.add_complex_class_rules_index(x); + } + if let Some(x) = args.misc_generic_selectors { + builder.add_misc_generic_selectors(x); + } + if let Some(x) = args.simple_id_rules { + builder.add_simple_id_rules(x); + } + if let Some(x) = args.simple_class_rules { + builder.add_simple_class_rules(x); + } + builder.finish() + } - /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. - #[inline] - pub fn simple_class_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_SIMPLE_CLASS_RULES, None).unwrap()} - } - /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. - #[inline] - pub fn simple_id_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_SIMPLE_ID_RULES, None).unwrap()} - } - /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit - /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` - #[inline] - pub fn misc_generic_selectors(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_MISC_GENERIC_SELECTORS, None).unwrap()} - } - /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` - /// These are stored as a multi-map from class name to list of selectors - #[inline] - pub fn complex_class_rules_index(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, None).unwrap()} - } - #[inline] - pub fn complex_class_rules_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, None).unwrap()} - } - /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` - /// These are stored as a multi-map from id name to list of selectors - #[inline] - pub fn complex_id_rules_index(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, None).unwrap()} - } - #[inline] - pub fn complex_id_rules_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, None).unwrap()} - } - /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors - #[inline] - pub fn hostname_hide_index(&self) -> flatbuffers::Vector<'a, u64> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, None).unwrap()} - } - #[inline] - pub fn hostname_hide_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, None).unwrap()} - } - /// Hostname-specific script injection filters - multi-map from hostname hash to script data - /// First byte of each script encodes permission bits to avoid separate permissions array - #[inline] - pub fn hostname_inject_script_index(&self) -> flatbuffers::Vector<'a, u64> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, None).unwrap()} - } - #[inline] - pub fn hostname_inject_script_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, None).unwrap()} - } - #[inline] - pub fn hostname_index(&self) -> flatbuffers::Vector<'a, u64> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(CosmeticFilters::VT_HOSTNAME_INDEX, None).unwrap()} - } - #[inline] - pub fn hostname_values(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(CosmeticFilters::VT_HOSTNAME_VALUES, None).unwrap()} - } -} + pub fn unpack(&self) -> CosmeticFiltersT { + let simple_class_rules = { + let x = self.simple_class_rules(); + x.iter().map(|s| s.to_string()).collect() + }; + let simple_id_rules = { + let x = self.simple_id_rules(); + x.iter().map(|s| s.to_string()).collect() + }; + let misc_generic_selectors = { + let x = self.misc_generic_selectors(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_class_rules_index = { + let x = self.complex_class_rules_index(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_class_rules_values = { + let x = self.complex_class_rules_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_id_rules_index = { + let x = self.complex_id_rules_index(); + x.iter().map(|s| s.to_string()).collect() + }; + let complex_id_rules_values = { + let x = self.complex_id_rules_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_hide_index = { + let x = self.hostname_hide_index(); + x.into_iter().collect() + }; + let hostname_hide_values = { + let x = self.hostname_hide_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_inject_script_index = { + let x = self.hostname_inject_script_index(); + x.into_iter().collect() + }; + let hostname_inject_script_values = { + let x = self.hostname_inject_script_values(); + x.iter().map(|s| s.to_string()).collect() + }; + let hostname_index = { + let x = self.hostname_index(); + x.into_iter().collect() + }; + let hostname_values = { + let x = self.hostname_values(); + x.iter().map(|t| t.unpack()).collect() + }; + CosmeticFiltersT { + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index, + complex_class_rules_values, + complex_id_rules_index, + complex_id_rules_values, + hostname_hide_index, + hostname_hide_values, + hostname_inject_script_index, + hostname_inject_script_values, + hostname_index, + hostname_values, + } + } -impl flatbuffers::Verifiable for CosmeticFilters<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>>("simple_class_rules", Self::VT_SIMPLE_CLASS_RULES, true)? - .visit_field::>>>("simple_id_rules", Self::VT_SIMPLE_ID_RULES, true)? - .visit_field::>>>("misc_generic_selectors", Self::VT_MISC_GENERIC_SELECTORS, true)? - .visit_field::>>>("complex_class_rules_index", Self::VT_COMPLEX_CLASS_RULES_INDEX, true)? - .visit_field::>>>("complex_class_rules_values", Self::VT_COMPLEX_CLASS_RULES_VALUES, true)? - .visit_field::>>>("complex_id_rules_index", Self::VT_COMPLEX_ID_RULES_INDEX, true)? - .visit_field::>>>("complex_id_rules_values", Self::VT_COMPLEX_ID_RULES_VALUES, true)? - .visit_field::>>("hostname_hide_index", Self::VT_HOSTNAME_HIDE_INDEX, true)? - .visit_field::>>>("hostname_hide_values", Self::VT_HOSTNAME_HIDE_VALUES, true)? - .visit_field::>>("hostname_inject_script_index", Self::VT_HOSTNAME_INJECT_SCRIPT_INDEX, true)? - .visit_field::>>>("hostname_inject_script_values", Self::VT_HOSTNAME_INJECT_SCRIPT_VALUES, true)? - .visit_field::>>("hostname_index", Self::VT_HOSTNAME_INDEX, true)? - .visit_field::>>>("hostname_values", Self::VT_HOSTNAME_VALUES, true)? - .finish(); - Ok(()) - } -} -pub struct CosmeticFiltersArgs<'a> { - pub simple_class_rules: Option>>>, - pub simple_id_rules: Option>>>, - pub misc_generic_selectors: Option>>>, - pub complex_class_rules_index: Option>>>, - pub complex_class_rules_values: Option>>>, - pub complex_id_rules_index: Option>>>, - pub complex_id_rules_values: Option>>>, - pub hostname_hide_index: Option>>, - pub hostname_hide_values: Option>>>, - pub hostname_inject_script_index: Option>>, - pub hostname_inject_script_values: Option>>>, - pub hostname_index: Option>>, - pub hostname_values: Option>>>>, -} -impl<'a> Default for CosmeticFiltersArgs<'a> { - #[inline] - fn default() -> Self { - CosmeticFiltersArgs { - simple_class_rules: None, // required field - simple_id_rules: None, // required field - misc_generic_selectors: None, // required field - complex_class_rules_index: None, // required field - complex_class_rules_values: None, // required field - complex_id_rules_index: None, // required field - complex_id_rules_values: None, // required field - hostname_hide_index: None, // required field - hostname_hide_values: None, // required field - hostname_inject_script_index: None, // required field - hostname_inject_script_values: None, // required field - hostname_index: None, // required field - hostname_values: None, // required field - } - } -} + /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. + #[inline] + pub fn simple_class_rules( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_SIMPLE_CLASS_RULES, None) + .unwrap() + } + } + /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. + #[inline] + pub fn simple_id_rules( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_SIMPLE_ID_RULES, None) + .unwrap() + } + } + /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit + /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` + #[inline] + pub fn misc_generic_selectors( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_MISC_GENERIC_SELECTORS, None) + .unwrap() + } + } + /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` + /// These are stored as a multi-map from class name to list of selectors + #[inline] + pub fn complex_class_rules_index( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, None) + .unwrap() + } + } + #[inline] + pub fn complex_class_rules_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, None) + .unwrap() + } + } + /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` + /// These are stored as a multi-map from id name to list of selectors + #[inline] + pub fn complex_id_rules_index( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, None) + .unwrap() + } + } + #[inline] + pub fn complex_id_rules_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, None) + .unwrap() + } + } + /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors + #[inline] + pub fn hostname_hide_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, + None, + ) + .unwrap() + } + } + #[inline] + pub fn hostname_hide_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, None) + .unwrap() + } + } + /// Hostname-specific script injection filters - multi-map from hostname hash to script data + /// First byte of each script encodes permission bits to avoid separate permissions array + #[inline] + pub fn hostname_inject_script_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, + None, + ) + .unwrap() + } + } + #[inline] + pub fn hostname_inject_script_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset<&'a str>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, None) + .unwrap() + } + } + #[inline] + pub fn hostname_index(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + CosmeticFilters::VT_HOSTNAME_INDEX, + None, + ) + .unwrap() + } + } + #[inline] + pub fn hostname_values( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> + { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::, + >, + >>(CosmeticFilters::VT_HOSTNAME_VALUES, None) + .unwrap() + } + } + } -pub struct CosmeticFiltersBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> CosmeticFiltersBuilder<'a, 'b, A> { - #[inline] - pub fn add_simple_class_rules(&mut self, simple_class_rules: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_SIMPLE_CLASS_RULES, simple_class_rules); - } - #[inline] - pub fn add_simple_id_rules(&mut self, simple_id_rules: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_SIMPLE_ID_RULES, simple_id_rules); - } - #[inline] - pub fn add_misc_generic_selectors(&mut self, misc_generic_selectors: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_MISC_GENERIC_SELECTORS, misc_generic_selectors); - } - #[inline] - pub fn add_complex_class_rules_index(&mut self, complex_class_rules_index: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, complex_class_rules_index); - } - #[inline] - pub fn add_complex_class_rules_values(&mut self, complex_class_rules_values: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, complex_class_rules_values); - } - #[inline] - pub fn add_complex_id_rules_index(&mut self, complex_id_rules_index: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, complex_id_rules_index); - } - #[inline] - pub fn add_complex_id_rules_values(&mut self, complex_id_rules_values: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, complex_id_rules_values); - } - #[inline] - pub fn add_hostname_hide_index(&mut self, hostname_hide_index: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, hostname_hide_index); - } - #[inline] - pub fn add_hostname_hide_values(&mut self, hostname_hide_values: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, hostname_hide_values); - } - #[inline] - pub fn add_hostname_inject_script_index(&mut self, hostname_inject_script_index: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, hostname_inject_script_index); - } - #[inline] - pub fn add_hostname_inject_script_values(&mut self, hostname_inject_script_values: flatbuffers::WIPOffset>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, hostname_inject_script_values); - } - #[inline] - pub fn add_hostname_index(&mut self, hostname_index: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_INDEX, hostname_index); - } - #[inline] - pub fn add_hostname_values(&mut self, hostname_values: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(CosmeticFilters::VT_HOSTNAME_VALUES, hostname_values); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> CosmeticFiltersBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - CosmeticFiltersBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, CosmeticFilters::VT_SIMPLE_CLASS_RULES,"simple_class_rules"); - self.fbb_.required(o, CosmeticFilters::VT_SIMPLE_ID_RULES,"simple_id_rules"); - self.fbb_.required(o, CosmeticFilters::VT_MISC_GENERIC_SELECTORS,"misc_generic_selectors"); - self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX,"complex_class_rules_index"); - self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES,"complex_class_rules_values"); - self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX,"complex_id_rules_index"); - self.fbb_.required(o, CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES,"complex_id_rules_values"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_HIDE_INDEX,"hostname_hide_index"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_HIDE_VALUES,"hostname_hide_values"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX,"hostname_inject_script_index"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES,"hostname_inject_script_values"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_INDEX,"hostname_index"); - self.fbb_.required(o, CosmeticFilters::VT_HOSTNAME_VALUES,"hostname_values"); - flatbuffers::WIPOffset::new(o.value()) - } -} + impl flatbuffers::Verifiable for CosmeticFilters<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>, + >>("simple_class_rules", Self::VT_SIMPLE_CLASS_RULES, true)? + .visit_field::>, + >>("simple_id_rules", Self::VT_SIMPLE_ID_RULES, true)? + .visit_field::>, + >>( + "misc_generic_selectors", + Self::VT_MISC_GENERIC_SELECTORS, + true, + )? + .visit_field::>, + >>( + "complex_class_rules_index", + Self::VT_COMPLEX_CLASS_RULES_INDEX, + true, + )? + .visit_field::>, + >>( + "complex_class_rules_values", + Self::VT_COMPLEX_CLASS_RULES_VALUES, + true, + )? + .visit_field::>, + >>( + "complex_id_rules_index", + Self::VT_COMPLEX_ID_RULES_INDEX, + true, + )? + .visit_field::>, + >>( + "complex_id_rules_values", + Self::VT_COMPLEX_ID_RULES_VALUES, + true, + )? + .visit_field::>>( + "hostname_hide_index", + Self::VT_HOSTNAME_HIDE_INDEX, + true, + )? + .visit_field::>, + >>("hostname_hide_values", Self::VT_HOSTNAME_HIDE_VALUES, true)? + .visit_field::>>( + "hostname_inject_script_index", + Self::VT_HOSTNAME_INJECT_SCRIPT_INDEX, + true, + )? + .visit_field::>, + >>( + "hostname_inject_script_values", + Self::VT_HOSTNAME_INJECT_SCRIPT_VALUES, + true, + )? + .visit_field::>>( + "hostname_index", + Self::VT_HOSTNAME_INDEX, + true, + )? + .visit_field::>, + >>("hostname_values", Self::VT_HOSTNAME_VALUES, true)? + .finish(); + Ok(()) + } + } + pub struct CosmeticFiltersArgs<'a> { + pub simple_class_rules: Option< + flatbuffers::WIPOffset>>, + >, + pub simple_id_rules: Option< + flatbuffers::WIPOffset>>, + >, + pub misc_generic_selectors: Option< + flatbuffers::WIPOffset>>, + >, + pub complex_class_rules_index: Option< + flatbuffers::WIPOffset>>, + >, + pub complex_class_rules_values: Option< + flatbuffers::WIPOffset>>, + >, + pub complex_id_rules_index: Option< + flatbuffers::WIPOffset>>, + >, + pub complex_id_rules_values: Option< + flatbuffers::WIPOffset>>, + >, + pub hostname_hide_index: Option>>, + pub hostname_hide_values: Option< + flatbuffers::WIPOffset>>, + >, + pub hostname_inject_script_index: + Option>>, + pub hostname_inject_script_values: Option< + flatbuffers::WIPOffset>>, + >, + pub hostname_index: Option>>, + pub hostname_values: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + } + impl<'a> Default for CosmeticFiltersArgs<'a> { + #[inline] + fn default() -> Self { + CosmeticFiltersArgs { + simple_class_rules: None, // required field + simple_id_rules: None, // required field + misc_generic_selectors: None, // required field + complex_class_rules_index: None, // required field + complex_class_rules_values: None, // required field + complex_id_rules_index: None, // required field + complex_id_rules_values: None, // required field + hostname_hide_index: None, // required field + hostname_hide_values: None, // required field + hostname_inject_script_index: None, // required field + hostname_inject_script_values: None, // required field + hostname_index: None, // required field + hostname_values: None, // required field + } + } + } -impl core::fmt::Debug for CosmeticFilters<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("CosmeticFilters"); - ds.field("simple_class_rules", &self.simple_class_rules()); - ds.field("simple_id_rules", &self.simple_id_rules()); - ds.field("misc_generic_selectors", &self.misc_generic_selectors()); - ds.field("complex_class_rules_index", &self.complex_class_rules_index()); - ds.field("complex_class_rules_values", &self.complex_class_rules_values()); - ds.field("complex_id_rules_index", &self.complex_id_rules_index()); - ds.field("complex_id_rules_values", &self.complex_id_rules_values()); - ds.field("hostname_hide_index", &self.hostname_hide_index()); - ds.field("hostname_hide_values", &self.hostname_hide_values()); - ds.field("hostname_inject_script_index", &self.hostname_inject_script_index()); - ds.field("hostname_inject_script_values", &self.hostname_inject_script_values()); - ds.field("hostname_index", &self.hostname_index()); - ds.field("hostname_values", &self.hostname_values()); - ds.finish() - } -} -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub struct CosmeticFiltersT { - pub simple_class_rules: Vec, - pub simple_id_rules: Vec, - pub misc_generic_selectors: Vec, - pub complex_class_rules_index: Vec, - pub complex_class_rules_values: Vec, - pub complex_id_rules_index: Vec, - pub complex_id_rules_values: Vec, - pub hostname_hide_index: Vec, - pub hostname_hide_values: Vec, - pub hostname_inject_script_index: Vec, - pub hostname_inject_script_values: Vec, - pub hostname_index: Vec, - pub hostname_values: Vec, -} -impl Default for CosmeticFiltersT { - fn default() -> Self { - Self { - simple_class_rules: Default::default(), - simple_id_rules: Default::default(), - misc_generic_selectors: Default::default(), - complex_class_rules_index: Default::default(), - complex_class_rules_values: Default::default(), - complex_id_rules_index: Default::default(), - complex_id_rules_values: Default::default(), - hostname_hide_index: Default::default(), - hostname_hide_values: Default::default(), - hostname_inject_script_index: Default::default(), - hostname_inject_script_values: Default::default(), - hostname_index: Default::default(), - hostname_values: Default::default(), - } - } -} -impl CosmeticFiltersT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> - ) -> flatbuffers::WIPOffset> { - let simple_class_rules = Some({ - let x = &self.simple_class_rules; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let simple_id_rules = Some({ - let x = &self.simple_id_rules; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let misc_generic_selectors = Some({ - let x = &self.misc_generic_selectors; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let complex_class_rules_index = Some({ - let x = &self.complex_class_rules_index; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let complex_class_rules_values = Some({ - let x = &self.complex_class_rules_values; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let complex_id_rules_index = Some({ - let x = &self.complex_id_rules_index; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let complex_id_rules_values = Some({ - let x = &self.complex_id_rules_values; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let hostname_hide_index = Some({ - let x = &self.hostname_hide_index; - _fbb.create_vector(x) - }); - let hostname_hide_values = Some({ - let x = &self.hostname_hide_values; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let hostname_inject_script_index = Some({ - let x = &self.hostname_inject_script_index; - _fbb.create_vector(x) - }); - let hostname_inject_script_values = Some({ - let x = &self.hostname_inject_script_values; - let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect();_fbb.create_vector(&w) - }); - let hostname_index = Some({ - let x = &self.hostname_index; - _fbb.create_vector(x) - }); - let hostname_values = Some({ - let x = &self.hostname_values; - let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) - }); - CosmeticFilters::create(_fbb, &CosmeticFiltersArgs{ - simple_class_rules, - simple_id_rules, - misc_generic_selectors, - complex_class_rules_index, - complex_class_rules_values, - complex_id_rules_index, - complex_id_rules_values, - hostname_hide_index, - hostname_hide_values, - hostname_inject_script_index, - hostname_inject_script_values, - hostname_index, - hostname_values, - }) - } -} -pub enum EngineOffset {} -#[derive(Copy, Clone, PartialEq)] + pub struct CosmeticFiltersBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> CosmeticFiltersBuilder<'a, 'b, A> { + #[inline] + pub fn add_simple_class_rules( + &mut self, + simple_class_rules: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_SIMPLE_CLASS_RULES, + simple_class_rules, + ); + } + #[inline] + pub fn add_simple_id_rules( + &mut self, + simple_id_rules: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_SIMPLE_ID_RULES, + simple_id_rules, + ); + } + #[inline] + pub fn add_misc_generic_selectors( + &mut self, + misc_generic_selectors: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_MISC_GENERIC_SELECTORS, + misc_generic_selectors, + ); + } + #[inline] + pub fn add_complex_class_rules_index( + &mut self, + complex_class_rules_index: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, + complex_class_rules_index, + ); + } + #[inline] + pub fn add_complex_class_rules_values( + &mut self, + complex_class_rules_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, + complex_class_rules_values, + ); + } + #[inline] + pub fn add_complex_id_rules_index( + &mut self, + complex_id_rules_index: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, + complex_id_rules_index, + ); + } + #[inline] + pub fn add_complex_id_rules_values( + &mut self, + complex_id_rules_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, + complex_id_rules_values, + ); + } + #[inline] + pub fn add_hostname_hide_index( + &mut self, + hostname_hide_index: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, + hostname_hide_index, + ); + } + #[inline] + pub fn add_hostname_hide_values( + &mut self, + hostname_hide_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, + hostname_hide_values, + ); + } + #[inline] + pub fn add_hostname_inject_script_index( + &mut self, + hostname_inject_script_index: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, + hostname_inject_script_index, + ); + } + #[inline] + pub fn add_hostname_inject_script_values( + &mut self, + hostname_inject_script_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset<&'b str>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, + hostname_inject_script_values, + ); + } + #[inline] + pub fn add_hostname_index( + &mut self, + hostname_index: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_INDEX, + hostname_index, + ); + } + #[inline] + pub fn add_hostname_values( + &mut self, + hostname_values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_.push_slot_always::>( + CosmeticFilters::VT_HOSTNAME_VALUES, + hostname_values, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> CosmeticFiltersBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + CosmeticFiltersBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required( + o, + CosmeticFilters::VT_SIMPLE_CLASS_RULES, + "simple_class_rules", + ); + self.fbb_ + .required(o, CosmeticFilters::VT_SIMPLE_ID_RULES, "simple_id_rules"); + self.fbb_.required( + o, + CosmeticFilters::VT_MISC_GENERIC_SELECTORS, + "misc_generic_selectors", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_COMPLEX_CLASS_RULES_INDEX, + "complex_class_rules_index", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_COMPLEX_CLASS_RULES_VALUES, + "complex_class_rules_values", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_COMPLEX_ID_RULES_INDEX, + "complex_id_rules_index", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_COMPLEX_ID_RULES_VALUES, + "complex_id_rules_values", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_HOSTNAME_HIDE_INDEX, + "hostname_hide_index", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_HOSTNAME_HIDE_VALUES, + "hostname_hide_values", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_INDEX, + "hostname_inject_script_index", + ); + self.fbb_.required( + o, + CosmeticFilters::VT_HOSTNAME_INJECT_SCRIPT_VALUES, + "hostname_inject_script_values", + ); + self.fbb_ + .required(o, CosmeticFilters::VT_HOSTNAME_INDEX, "hostname_index"); + self.fbb_ + .required(o, CosmeticFilters::VT_HOSTNAME_VALUES, "hostname_values"); + flatbuffers::WIPOffset::new(o.value()) + } + } -pub struct Engine<'a> { - pub _tab: flatbuffers::Table<'a>, -} + impl core::fmt::Debug for CosmeticFilters<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("CosmeticFilters"); + ds.field("simple_class_rules", &self.simple_class_rules()); + ds.field("simple_id_rules", &self.simple_id_rules()); + ds.field("misc_generic_selectors", &self.misc_generic_selectors()); + ds.field( + "complex_class_rules_index", + &self.complex_class_rules_index(), + ); + ds.field( + "complex_class_rules_values", + &self.complex_class_rules_values(), + ); + ds.field("complex_id_rules_index", &self.complex_id_rules_index()); + ds.field("complex_id_rules_values", &self.complex_id_rules_values()); + ds.field("hostname_hide_index", &self.hostname_hide_index()); + ds.field("hostname_hide_values", &self.hostname_hide_values()); + ds.field( + "hostname_inject_script_index", + &self.hostname_inject_script_index(), + ); + ds.field( + "hostname_inject_script_values", + &self.hostname_inject_script_values(), + ); + ds.field("hostname_index", &self.hostname_index()); + ds.field("hostname_values", &self.hostname_values()); + ds.finish() + } + } + #[non_exhaustive] + #[derive(Debug, Clone, PartialEq)] + pub struct CosmeticFiltersT { + pub simple_class_rules: Vec, + pub simple_id_rules: Vec, + pub misc_generic_selectors: Vec, + pub complex_class_rules_index: Vec, + pub complex_class_rules_values: Vec, + pub complex_id_rules_index: Vec, + pub complex_id_rules_values: Vec, + pub hostname_hide_index: Vec, + pub hostname_hide_values: Vec, + pub hostname_inject_script_index: Vec, + pub hostname_inject_script_values: Vec, + pub hostname_index: Vec, + pub hostname_values: Vec, + } + impl Default for CosmeticFiltersT { + fn default() -> Self { + Self { + simple_class_rules: Default::default(), + simple_id_rules: Default::default(), + misc_generic_selectors: Default::default(), + complex_class_rules_index: Default::default(), + complex_class_rules_values: Default::default(), + complex_id_rules_index: Default::default(), + complex_id_rules_values: Default::default(), + hostname_hide_index: Default::default(), + hostname_hide_values: Default::default(), + hostname_inject_script_index: Default::default(), + hostname_inject_script_values: Default::default(), + hostname_index: Default::default(), + hostname_values: Default::default(), + } + } + } + impl CosmeticFiltersT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, + ) -> flatbuffers::WIPOffset> { + let simple_class_rules = Some({ + let x = &self.simple_class_rules; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let simple_id_rules = Some({ + let x = &self.simple_id_rules; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let misc_generic_selectors = Some({ + let x = &self.misc_generic_selectors; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let complex_class_rules_index = Some({ + let x = &self.complex_class_rules_index; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let complex_class_rules_values = Some({ + let x = &self.complex_class_rules_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let complex_id_rules_index = Some({ + let x = &self.complex_id_rules_index; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let complex_id_rules_values = Some({ + let x = &self.complex_id_rules_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let hostname_hide_index = Some({ + let x = &self.hostname_hide_index; + _fbb.create_vector(x) + }); + let hostname_hide_values = Some({ + let x = &self.hostname_hide_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let hostname_inject_script_index = Some({ + let x = &self.hostname_inject_script_index; + _fbb.create_vector(x) + }); + let hostname_inject_script_values = Some({ + let x = &self.hostname_inject_script_values; + let w: Vec<_> = x.iter().map(|s| _fbb.create_string(s)).collect(); + _fbb.create_vector(&w) + }); + let hostname_index = Some({ + let x = &self.hostname_index; + _fbb.create_vector(x) + }); + let hostname_values = Some({ + let x = &self.hostname_values; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); + _fbb.create_vector(&w) + }); + CosmeticFilters::create( + _fbb, + &CosmeticFiltersArgs { + simple_class_rules, + simple_id_rules, + misc_generic_selectors, + complex_class_rules_index, + complex_class_rules_values, + complex_id_rules_index, + complex_id_rules_values, + hostname_hide_index, + hostname_hide_values, + hostname_inject_script_index, + hostname_inject_script_values, + hostname_index, + hostname_values, + }, + ) + } + } + pub enum EngineOffset {} + #[derive(Copy, Clone, PartialEq)] -impl<'a> flatbuffers::Follow<'a> for Engine<'a> { - type Inner = Engine<'a>; - #[inline] - unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table::new(buf, loc) } - } -} + pub struct Engine<'a> { + pub _tab: flatbuffers::Table<'a>, + } -impl<'a> Engine<'a> { - pub const VT_VERSION: flatbuffers::VOffsetT = 4; - pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 6; - pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 8; - pub const VT_COSMETIC_FILTERS: flatbuffers::VOffsetT = 10; + impl<'a> flatbuffers::Follow<'a> for Engine<'a> { + type Inner = Engine<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } + } - #[inline] - pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Engine { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args EngineArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = EngineBuilder::new(_fbb); - if let Some(x) = args.cosmetic_filters { builder.add_cosmetic_filters(x); } - if let Some(x) = args.unique_domains_hashes { builder.add_unique_domains_hashes(x); } - if let Some(x) = args.network_rules { builder.add_network_rules(x); } - builder.add_version(args.version); - builder.finish() - } + impl<'a> Engine<'a> { + pub const VT_VERSION: flatbuffers::VOffsetT = 4; + pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 6; + pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 8; + pub const VT_COSMETIC_FILTERS: flatbuffers::VOffsetT = 10; - pub fn unpack(&self) -> EngineT { - let version = self.version(); - let network_rules = { - let x = self.network_rules(); - x.iter().map(|t| t.unpack()).collect() - }; - let unique_domains_hashes = { - let x = self.unique_domains_hashes(); - x.into_iter().collect() - }; - let cosmetic_filters = { - let x = self.cosmetic_filters(); - Box::new(x.unpack()) - }; - EngineT { - version, - network_rules, - unique_domains_hashes, - cosmetic_filters, - } - } + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Engine { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EngineArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = EngineBuilder::new(_fbb); + if let Some(x) = args.cosmetic_filters { + builder.add_cosmetic_filters(x); + } + if let Some(x) = args.unique_domains_hashes { + builder.add_unique_domains_hashes(x); + } + if let Some(x) = args.network_rules { + builder.add_network_rules(x); + } + builder.add_version(args.version); + builder.finish() + } - #[inline] - pub fn version(&self) -> u32 { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::(Engine::VT_VERSION, Some(0)).unwrap()} - } - #[inline] - pub fn network_rules(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>>(Engine::VT_NETWORK_RULES, None).unwrap()} - } - #[inline] - pub fn unique_domains_hashes(&self) -> flatbuffers::Vector<'a, u64> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>>(Engine::VT_UNIQUE_DOMAINS_HASHES, None).unwrap()} - } - #[inline] - pub fn cosmetic_filters(&self) -> CosmeticFilters<'a> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::>(Engine::VT_COSMETIC_FILTERS, None).unwrap()} - } -} + pub fn unpack(&self) -> EngineT { + let version = self.version(); + let network_rules = { + let x = self.network_rules(); + x.iter().map(|t| t.unpack()).collect() + }; + let unique_domains_hashes = { + let x = self.unique_domains_hashes(); + x.into_iter().collect() + }; + let cosmetic_filters = { + let x = self.cosmetic_filters(); + Box::new(x.unpack()) + }; + EngineT { + version, + network_rules, + unique_domains_hashes, + cosmetic_filters, + } + } -impl flatbuffers::Verifiable for Engine<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("version", Self::VT_VERSION, false)? - .visit_field::>>>("network_rules", Self::VT_NETWORK_RULES, true)? - .visit_field::>>("unique_domains_hashes", Self::VT_UNIQUE_DOMAINS_HASHES, true)? - .visit_field::>("cosmetic_filters", Self::VT_COSMETIC_FILTERS, true)? - .finish(); - Ok(()) - } -} -pub struct EngineArgs<'a> { - pub version: u32, - pub network_rules: Option>>>>, - pub unique_domains_hashes: Option>>, - pub cosmetic_filters: Option>>, -} -impl<'a> Default for EngineArgs<'a> { - #[inline] - fn default() -> Self { - EngineArgs { - version: 0, - network_rules: None, // required field - unique_domains_hashes: None, // required field - cosmetic_filters: None, // required field - } - } -} + #[inline] + pub fn version(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Engine::VT_VERSION, Some(0)).unwrap() } + } + #[inline] + pub fn network_rules( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Engine::VT_NETWORK_RULES, None) + .unwrap() + } + } + #[inline] + pub fn unique_domains_hashes(&self) -> flatbuffers::Vector<'a, u64> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Engine::VT_UNIQUE_DOMAINS_HASHES, + None, + ) + .unwrap() + } + } + #[inline] + pub fn cosmetic_filters(&self) -> CosmeticFilters<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>( + Engine::VT_COSMETIC_FILTERS, + None, + ) + .unwrap() + } + } + } -pub struct EngineBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EngineBuilder<'a, 'b, A> { - #[inline] - pub fn add_version(&mut self, version: u32) { - self.fbb_.push_slot::(Engine::VT_VERSION, version, 0); - } - #[inline] - pub fn add_network_rules(&mut self, network_rules: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Engine::VT_NETWORK_RULES, network_rules); - } - #[inline] - pub fn add_unique_domains_hashes(&mut self, unique_domains_hashes: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Engine::VT_UNIQUE_DOMAINS_HASHES, unique_domains_hashes); - } - #[inline] - pub fn add_cosmetic_filters(&mut self, cosmetic_filters: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Engine::VT_COSMETIC_FILTERS, cosmetic_filters); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> EngineBuilder<'a, 'b, A> { - let start = _fbb.start_table(); - EngineBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, Engine::VT_NETWORK_RULES,"network_rules"); - self.fbb_.required(o, Engine::VT_UNIQUE_DOMAINS_HASHES,"unique_domains_hashes"); - self.fbb_.required(o, Engine::VT_COSMETIC_FILTERS,"cosmetic_filters"); - flatbuffers::WIPOffset::new(o.value()) - } -} + impl flatbuffers::Verifiable for Engine<'_> { + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("version", Self::VT_VERSION, false)? + .visit_field::>, + >>("network_rules", Self::VT_NETWORK_RULES, true)? + .visit_field::>>( + "unique_domains_hashes", + Self::VT_UNIQUE_DOMAINS_HASHES, + true, + )? + .visit_field::>( + "cosmetic_filters", + Self::VT_COSMETIC_FILTERS, + true, + )? + .finish(); + Ok(()) + } + } + pub struct EngineArgs<'a> { + pub version: u32, + pub network_rules: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub unique_domains_hashes: Option>>, + pub cosmetic_filters: Option>>, + } + impl<'a> Default for EngineArgs<'a> { + #[inline] + fn default() -> Self { + EngineArgs { + version: 0, + network_rules: None, // required field + unique_domains_hashes: None, // required field + cosmetic_filters: None, // required field + } + } + } -impl core::fmt::Debug for Engine<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Engine"); - ds.field("version", &self.version()); - ds.field("network_rules", &self.network_rules()); - ds.field("unique_domains_hashes", &self.unique_domains_hashes()); - ds.field("cosmetic_filters", &self.cosmetic_filters()); - ds.finish() - } -} -#[non_exhaustive] -#[derive(Debug, Clone, PartialEq)] -pub struct EngineT { - pub version: u32, - pub network_rules: Vec, - pub unique_domains_hashes: Vec, - pub cosmetic_filters: Box, -} -impl Default for EngineT { - fn default() -> Self { - Self { - version: 0, - network_rules: Default::default(), - unique_domains_hashes: Default::default(), - cosmetic_filters: Default::default(), - } - } -} -impl EngineT { - pub fn pack<'b, A: flatbuffers::Allocator + 'b>( - &self, - _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A> - ) -> flatbuffers::WIPOffset> { - let version = self.version; - let network_rules = Some({ - let x = &self.network_rules; - let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect();_fbb.create_vector(&w) - }); - let unique_domains_hashes = Some({ - let x = &self.unique_domains_hashes; - _fbb.create_vector(x) - }); - let cosmetic_filters = Some({ - let x = &self.cosmetic_filters; - x.pack(_fbb) - }); - Engine::create(_fbb, &EngineArgs{ - version, - network_rules, - unique_domains_hashes, - cosmetic_filters, - }) - } -} -#[inline] -/// Verifies that a buffer of bytes contains a `Engine` -/// and returns it. -/// Note that verification is still experimental and may not -/// catch every error, or be maximally performant. For the -/// previous, unchecked, behavior use -/// `root_as_engine_unchecked`. -pub fn root_as_engine(buf: &[u8]) -> Result { - flatbuffers::root::(buf) -} -#[inline] -/// Verifies that a buffer of bytes contains a size prefixed -/// `Engine` and returns it. -/// Note that verification is still experimental and may not -/// catch every error, or be maximally performant. For the -/// previous, unchecked, behavior use -/// `size_prefixed_root_as_engine_unchecked`. -pub fn size_prefixed_root_as_engine(buf: &[u8]) -> Result { - flatbuffers::size_prefixed_root::(buf) -} -#[inline] -/// Verifies, with the given options, that a buffer of bytes -/// contains a `Engine` and returns it. -/// Note that verification is still experimental and may not -/// catch every error, or be maximally performant. For the -/// previous, unchecked, behavior use -/// `root_as_engine_unchecked`. -pub fn root_as_engine_with_opts<'b, 'o>( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], -) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::root_with_opts::>(opts, buf) -} -#[inline] -/// Verifies, with the given verifier options, that a buffer of -/// bytes contains a size prefixed `Engine` and returns -/// it. Note that verification is still experimental and may not -/// catch every error, or be maximally performant. For the -/// previous, unchecked, behavior use -/// `root_as_engine_unchecked`. -pub fn size_prefixed_root_as_engine_with_opts<'b, 'o>( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], -) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::size_prefixed_root_with_opts::>(opts, buf) -} -#[inline] -/// Assumes, without verification, that a buffer of bytes contains a Engine and returns it. -/// # Safety -/// Callers must trust the given bytes do indeed contain a valid `Engine`. -pub unsafe fn root_as_engine_unchecked(buf: &[u8]) -> Engine { - flatbuffers::root_unchecked::(buf) -} -#[inline] -/// Assumes, without verification, that a buffer of bytes contains a size prefixed Engine and returns it. -/// # Safety -/// Callers must trust the given bytes do indeed contain a valid size prefixed `Engine`. -pub unsafe fn size_prefixed_root_as_engine_unchecked(buf: &[u8]) -> Engine { - flatbuffers::size_prefixed_root_unchecked::(buf) -} -#[inline] -pub fn finish_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( - fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, - root: flatbuffers::WIPOffset>) { - fbb.finish(root, None); -} + pub struct EngineBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, + } + impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EngineBuilder<'a, 'b, A> { + #[inline] + pub fn add_version(&mut self, version: u32) { + self.fbb_.push_slot::(Engine::VT_VERSION, version, 0); + } + #[inline] + pub fn add_network_rules( + &mut self, + network_rules: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_.push_slot_always::>( + Engine::VT_NETWORK_RULES, + network_rules, + ); + } + #[inline] + pub fn add_unique_domains_hashes( + &mut self, + unique_domains_hashes: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + Engine::VT_UNIQUE_DOMAINS_HASHES, + unique_domains_hashes, + ); + } + #[inline] + pub fn add_cosmetic_filters( + &mut self, + cosmetic_filters: flatbuffers::WIPOffset>, + ) { + self.fbb_ + .push_slot_always::>( + Engine::VT_COSMETIC_FILTERS, + cosmetic_filters, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> EngineBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EngineBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_ + .required(o, Engine::VT_NETWORK_RULES, "network_rules"); + self.fbb_ + .required(o, Engine::VT_UNIQUE_DOMAINS_HASHES, "unique_domains_hashes"); + self.fbb_ + .required(o, Engine::VT_COSMETIC_FILTERS, "cosmetic_filters"); + flatbuffers::WIPOffset::new(o.value()) + } + } -#[inline] -pub fn finish_size_prefixed_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { - fbb.finish_size_prefixed(root, None); -} -} // pub mod fb + impl core::fmt::Debug for Engine<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Engine"); + ds.field("version", &self.version()); + ds.field("network_rules", &self.network_rules()); + ds.field("unique_domains_hashes", &self.unique_domains_hashes()); + ds.field("cosmetic_filters", &self.cosmetic_filters()); + ds.finish() + } + } + #[non_exhaustive] + #[derive(Debug, Clone, PartialEq)] + pub struct EngineT { + pub version: u32, + pub network_rules: Vec, + pub unique_domains_hashes: Vec, + pub cosmetic_filters: Box, + } + impl Default for EngineT { + fn default() -> Self { + Self { + version: 0, + network_rules: Default::default(), + unique_domains_hashes: Default::default(), + cosmetic_filters: Default::default(), + } + } + } + impl EngineT { + pub fn pack<'b, A: flatbuffers::Allocator + 'b>( + &self, + _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, + ) -> flatbuffers::WIPOffset> { + let version = self.version; + let network_rules = Some({ + let x = &self.network_rules; + let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); + _fbb.create_vector(&w) + }); + let unique_domains_hashes = Some({ + let x = &self.unique_domains_hashes; + _fbb.create_vector(x) + }); + let cosmetic_filters = Some({ + let x = &self.cosmetic_filters; + x.pack(_fbb) + }); + Engine::create( + _fbb, + &EngineArgs { + version, + network_rules, + unique_domains_hashes, + cosmetic_filters, + }, + ) + } + } + #[inline] + /// Verifies that a buffer of bytes contains a `Engine` + /// and returns it. + /// Note that verification is still experimental and may not + /// catch every error, or be maximally performant. For the + /// previous, unchecked, behavior use + /// `root_as_engine_unchecked`. + pub fn root_as_engine(buf: &[u8]) -> Result { + flatbuffers::root::(buf) + } + #[inline] + /// Verifies that a buffer of bytes contains a size prefixed + /// `Engine` and returns it. + /// Note that verification is still experimental and may not + /// catch every error, or be maximally performant. For the + /// previous, unchecked, behavior use + /// `size_prefixed_root_as_engine_unchecked`. + pub fn size_prefixed_root_as_engine( + buf: &[u8], + ) -> Result { + flatbuffers::size_prefixed_root::(buf) + } + #[inline] + /// Verifies, with the given options, that a buffer of bytes + /// contains a `Engine` and returns it. + /// Note that verification is still experimental and may not + /// catch every error, or be maximally performant. For the + /// previous, unchecked, behavior use + /// `root_as_engine_unchecked`. + pub fn root_as_engine_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], + ) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::root_with_opts::>(opts, buf) + } + #[inline] + /// Verifies, with the given verifier options, that a buffer of + /// bytes contains a size prefixed `Engine` and returns + /// it. Note that verification is still experimental and may not + /// catch every error, or be maximally performant. For the + /// previous, unchecked, behavior use + /// `root_as_engine_unchecked`. + pub fn size_prefixed_root_as_engine_with_opts<'b, 'o>( + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], + ) -> Result, flatbuffers::InvalidFlatbuffer> { + flatbuffers::size_prefixed_root_with_opts::>(opts, buf) + } + #[inline] + /// Assumes, without verification, that a buffer of bytes contains a Engine and returns it. + /// # Safety + /// Callers must trust the given bytes do indeed contain a valid `Engine`. + pub unsafe fn root_as_engine_unchecked(buf: &[u8]) -> Engine { + flatbuffers::root_unchecked::(buf) + } + #[inline] + /// Assumes, without verification, that a buffer of bytes contains a size prefixed Engine and returns it. + /// # Safety + /// Callers must trust the given bytes do indeed contain a valid size prefixed `Engine`. + pub unsafe fn size_prefixed_root_as_engine_unchecked(buf: &[u8]) -> Engine { + flatbuffers::size_prefixed_root_unchecked::(buf) + } + #[inline] + pub fn finish_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + root: flatbuffers::WIPOffset>, + ) { + fbb.finish(root, None); + } + #[inline] + pub fn finish_size_prefixed_engine_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + root: flatbuffers::WIPOffset>, + ) { + fbb.finish_size_prefixed(root, None); + } +} // pub mod fb From d87eeb37b3cb6c433c4b6bab95b20d9714db80e1 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Tue, 26 Aug 2025 14:11:28 +0700 Subject: [PATCH 08/21] Fix cargo clippy --- src/cosmetic_filter_cache_builder.rs | 8 ++++---- src/filters/fb_network_builder.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs index 61c91111..445617fc 100644 --- a/src/cosmetic_filter_cache_builder.rs +++ b/src/cosmetic_filter_cache_builder.rs @@ -184,28 +184,28 @@ impl CosmeticFilterCacheBuilder { let entry = self .specific_rules .entry(*token) - .or_insert_with(HostnameRule::default); + .or_default(); entry.unhide.push(s); } UninjectScript((s, _)) => { let entry = self .specific_rules .entry(*token) - .or_insert_with(HostnameRule::default); + .or_default(); entry.uninject_script.push(s); } ProceduralOrAction(s) => { let entry = self .specific_rules .entry(*token) - .or_insert_with(HostnameRule::default); + .or_default(); entry.procedural_action.push(s); } ProceduralOrActionException(s) => { let entry = self .specific_rules .entry(*token) - .or_insert_with(HostnameRule::default); + .or_default(); entry.procedural_action_exception.push(s); } } diff --git a/src/filters/fb_network_builder.rs b/src/filters/fb_network_builder.rs index 28d37126..fb1d0360 100644 --- a/src/filters/fb_network_builder.rs +++ b/src/filters/fb_network_builder.rs @@ -97,7 +97,7 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { .map(|v| builder.create_string(v.as_str())); let network_filter = fb::NetworkFilter::create( - &mut builder.raw_builder(), + builder.raw_builder(), &fb::NetworkFilterArgs { mask: network_filter.mask.bits(), patterns, From db99da0d56cc612fd6482e29b6052dd967fb752e Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Tue, 26 Aug 2025 14:50:29 +0700 Subject: [PATCH 09/21] Fix cargo fmt & some review issues --- src/cosmetic_filter_cache.rs | 42 ++++++++++++++++------------ src/cosmetic_filter_cache_builder.rs | 20 +++---------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index ffe57aff..9d384b67 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -166,15 +166,17 @@ impl CosmeticFilterCache { ) -> Vec { let mut selectors = vec![]; - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let simple_class_rules = FlatSetView::new(cs.simple_class_rules()); - let simple_id_rules = FlatSetView::new(cs.simple_id_rules()); + let cosmetic_filters = self.filter_data_context.memory.root().cosmetic_filters(); + let simple_class_rules = FlatSetView::new(cosmetic_filters.simple_class_rules()); + let simple_id_rules = FlatSetView::new(cosmetic_filters.simple_id_rules()); let complex_class_rules = FlatMapStringView::new( - cs.complex_class_rules_index(), - cs.complex_class_rules_values(), + cosmetic_filters.complex_class_rules_index(), + cosmetic_filters.complex_class_rules_values(), + ); + let complex_id_rules = FlatMapStringView::new( + cosmetic_filters.complex_id_rules_index(), + cosmetic_filters.complex_id_rules_values(), ); - let complex_id_rules = - FlatMapStringView::new(cs.complex_id_rules_index(), cs.complex_id_rules_values()); classes.into_iter().for_each(|class| { let class = class.as_ref(); @@ -239,13 +241,18 @@ impl CosmeticFilterCache { .chain(request_hostnames.iter()) .collect(); - let cf = self.filter_data_context.memory.root().cosmetic_filters(); - let hostname_rules_view = FlatMapView::new(cf.hostname_index(), cf.hostname_values()); - let hostname_hide_view = - FlatMultiMapView::new(cf.hostname_hide_index(), cf.hostname_hide_values()); + let cosmetic_filters = self.filter_data_context.memory.root().cosmetic_filters(); + let hostname_rules_view = FlatMapView::new( + cosmetic_filters.hostname_index(), + cosmetic_filters.hostname_values(), + ); + let hostname_hide_view = FlatMultiMapView::new( + cosmetic_filters.hostname_hide_index(), + cosmetic_filters.hostname_hide_values(), + ); let hostname_inject_script_view = FlatMultiMapView::new( - cf.hostname_inject_script_index(), - cf.hostname_inject_script_values(), + cosmetic_filters.hostname_inject_script_index(), + cosmetic_filters.hostname_inject_script_values(), ); for hash in hashes.iter() { @@ -317,13 +324,12 @@ impl CosmeticFilterCache { let hide_selectors = if generichide { specific_hide_selectors } else { - let cs = self.filter_data_context.memory.root().cosmetic_filters(); - let misc_generic_selectors_vector = cs.misc_generic_selectors(); + let cosmetic_filters = self.filter_data_context.memory.root().cosmetic_filters(); + let misc_generic_selectors_vector = cosmetic_filters.misc_generic_selectors(); - // TODO: check performance of this + // Calculate the intersection of the two sets, O(n * log m) time let mut hide_selectors = HashSet::new(); - for i in 0..misc_generic_selectors_vector.len() { - let selector = misc_generic_selectors_vector.get(i); + for selector in misc_generic_selectors_vector.iter() { if !exceptions.contains(selector) { hide_selectors.insert(selector.to_string()); } diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs index 445617fc..0cb614c3 100644 --- a/src/cosmetic_filter_cache_builder.rs +++ b/src/cosmetic_filter_cache_builder.rs @@ -181,31 +181,19 @@ impl CosmeticFilterCacheBuilder { } // Handle remaining types through HostnameRule Unhide(s) => { - let entry = self - .specific_rules - .entry(*token) - .or_default(); + let entry = self.specific_rules.entry(*token).or_default(); entry.unhide.push(s); } UninjectScript((s, _)) => { - let entry = self - .specific_rules - .entry(*token) - .or_default(); + let entry = self.specific_rules.entry(*token).or_default(); entry.uninject_script.push(s); } ProceduralOrAction(s) => { - let entry = self - .specific_rules - .entry(*token) - .or_default(); + let entry = self.specific_rules.entry(*token).or_default(); entry.procedural_action.push(s); } ProceduralOrActionException(s) => { - let entry = self - .specific_rules - .entry(*token) - .or_default(); + let entry = self.specific_rules.entry(*token).or_default(); entry.procedural_action_exception.push(s); } } From 1b51596ece4fbd2beb001eb76de59a20751f0a80 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Tue, 26 Aug 2025 22:37:03 +0700 Subject: [PATCH 10/21] move make_flatbuffer to engine.rs --- src/blocker.rs | 17 ++++++------- src/cosmetic_filter_cache.rs | 11 +++++---- src/engine.rs | 48 +++++++++++++++++++++++------------- src/filters/fb_builder.rs | 22 ++--------------- src/filters/fb_network.rs | 9 ------- 5 files changed, 46 insertions(+), 61 deletions(-) diff --git a/src/blocker.rs b/src/blocker.rs index 21b50bc1..26d7cc2f 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -440,16 +440,13 @@ impl Blocker { network_filters: Vec, options: &BlockerOptions, ) -> Self { - use crate::filters::{fb_builder::make_flatbuffer, fb_network::FilterDataContext}; - - let memory = make_flatbuffer( - network_filters, - vec![], // no cosmetic filters for blocker test - options.enable_optimizations, - 0, - ); - let filter_data_context = FilterDataContext::new(memory); - Self::from_context(filter_data_context) + use crate::engine::Engine; + use crate::FilterSet; + + let mut filter_set = FilterSet::new(true); + filter_set.network_filters = network_filters; + let engine = Engine::from_filter_set(filter_set, options.enable_optimizations); + Self::from_context(engine.filter_data_context()) } pub fn use_tags(&mut self, tags: &[&str]) { diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 9d384b67..16f7087a 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -132,12 +132,13 @@ impl CosmeticFilterCache { #[cfg(test)] pub fn from_rules(rules: Vec) -> Self { - use crate::filters::{fb_builder::make_flatbuffer, fb_network::FilterDataContext}; + use crate::engine::Engine; + use crate::FilterSet; - let memory = make_flatbuffer(vec![], rules, true, 0); - - let filter_data_context = FilterDataContext::new(memory); - Self::from_context(filter_data_context) + let mut filter_set = FilterSet::new(true); + filter_set.cosmetic_filters = rules; + let engine = Engine::from_filter_set(filter_set, true); + engine.cosmetic_cache() } /// Generic class/id rules are by far the most common type of cosmetic filtering rule, and they diff --git a/src/engine.rs b/src/engine.rs index 7afbe6be..b8867254 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,8 +2,13 @@ use crate::blocker::{Blocker, BlockerResult}; use crate::cosmetic_filter_cache::{CosmeticFilterCache, UrlSpecificResources}; -use crate::filters::fb_builder::make_flatbuffer; +use crate::cosmetic_filter_cache_builder::CosmeticFilterCacheBuilder; +use crate::filters::cosmetic::CosmeticFilter; +use crate::filters::fb_builder::EngineFlatBuilder; use crate::filters::fb_network::{FilterDataContext, FilterDataContextRef}; +use crate::filters::fb_network_builder::NetworkRulesBuilder; +use crate::filters::network::NetworkFilter; +use crate::flatbuffers::containers::flat_serialize::FlatSerialize; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; use crate::lists::{FilterSet, ParseOptions}; use crate::regex_manager::RegexManagerDiscardPolicy; @@ -63,16 +68,7 @@ pub enum DeserializationError { impl Default for Engine { fn default() -> Self { - let filter_data_context = FilterDataContextRef::new(Default::default()); - - Self { - blocker: Blocker::from_context(FilterDataContextRef::clone(&filter_data_context)), - cosmetic_cache: CosmeticFilterCache::from_context(FilterDataContextRef::clone( - &filter_data_context, - )), - resources: ResourceStorage::default(), - filter_data_context, - } + Self::from_filter_set(FilterSet::new(false), false) } } @@ -106,6 +102,16 @@ impl Engine { Self::from_filter_set(filter_set, optimize) } + #[cfg(test)] + pub(crate) fn cosmetic_cache(self) -> CosmeticFilterCache { + self.cosmetic_cache + } + + #[cfg(test)] + pub(crate) fn filter_data_context(self) -> FilterDataContextRef { + self.filter_data_context + } + /// Loads rules from the given `FilterSet`. It is recommended to use a `FilterSet` when adding /// rules from multiple sources. pub fn from_filter_set(set: FilterSet, optimize: bool) -> Self { @@ -115,12 +121,7 @@ impl Engine { .. } = set; - let memory = make_flatbuffer( - network_filters, - cosmetic_filters, - optimize, - ADBLOCK_FLATBUFFER_VERSION, - ); + let memory = make_flatbuffer(network_filters, cosmetic_filters, optimize); let filter_data_context = FilterDataContext::new(memory); @@ -300,6 +301,19 @@ fn _assertions() { _assert_sync::(); } +fn make_flatbuffer( + network_filters: Vec, + cosmetic_filters: Vec, + optimize: bool, +) -> VerifiedFlatbufferMemory { + let mut builder = EngineFlatBuilder::default(); + let network_rules_builder = NetworkRulesBuilder::from_rules(network_filters, optimize); + let network_rules = FlatSerialize::serialize(network_rules_builder, &mut builder); + let cosmetic_rules = CosmeticFilterCacheBuilder::from_rules(cosmetic_filters); + let cosmetic_rules = FlatSerialize::serialize(cosmetic_rules, &mut builder); + builder.finish(network_rules, cosmetic_rules, ADBLOCK_FLATBUFFER_VERSION) +} + #[cfg(test)] #[path = "../tests/unit/engine.rs"] mod unit_tests; diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 04af17e2..72356b89 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -1,15 +1,11 @@ //! Builder for creating flatbuffer with serialized engine. -//! The entry point is `make_flatbuffer`. use std::collections::HashMap; use flatbuffers::WIPOffset; -use crate::cosmetic_filter_cache_builder::CosmeticFilterCacheBuilder; -use crate::filters::cosmetic::CosmeticFilter; -use crate::filters::fb_network_builder::{NetworkFilterListBuilder, NetworkRulesBuilder}; -use crate::filters::network::NetworkFilter; -use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, FlatSerialize, WIPFlatVec}; +use crate::filters::fb_network_builder::NetworkFilterListBuilder; +use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, WIPFlatVec}; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; use crate::utils::Hash; @@ -64,17 +60,3 @@ impl<'a> FlatBuilder<'a> for EngineFlatBuilder<'a> { &mut self.fb_builder } } - -pub fn make_flatbuffer( - network_filters: Vec, - cosmetic_filters: Vec, - optimize: bool, - version: u32, -) -> VerifiedFlatbufferMemory { - let mut builder = EngineFlatBuilder::default(); - let network_rules_builder = NetworkRulesBuilder::from_rules(network_filters, optimize); - let network_rules = FlatSerialize::serialize(network_rules_builder, &mut builder); - let cosmetic_rules = CosmeticFilterCacheBuilder::from_rules(cosmetic_filters); - let cosmetic_rules = FlatSerialize::serialize(cosmetic_rules, &mut builder); - builder.finish(network_rules, cosmetic_rules, version) -} diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index 2a57724f..5bb72399 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -87,15 +87,6 @@ pub(crate) struct FilterDataContext { pub(crate) unique_domains_hashes_map: HashMap, } -impl Default for FilterDataContext { - fn default() -> Self { - Self { - memory: crate::filters::fb_builder::make_flatbuffer(vec![], vec![], false, 0), - unique_domains_hashes_map: HashMap::new(), - } - } -} - impl FilterDataContext { pub(crate) fn new(memory: VerifiedFlatbufferMemory) -> FilterDataContextRef { // Reconstruct the unique_domains_hashes_map from the flatbuffer data From 72e699ff45f64075172503213388e8ec47e4a088 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Wed, 27 Aug 2025 02:40:24 +0700 Subject: [PATCH 11/21] update comments --- src/cosmetic_filter_cache.rs | 1 + src/cosmetic_filter_cache_builder.rs | 6 ++- src/cosmetic_filter_utils.rs | 3 ++ src/flatbuffers/fb_network_filter.fbs | 50 ++++++++++++++----- .../fb_network_filter_generated.rs | 35 ++++++++++--- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 16f7087a..8af401b2 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -7,6 +7,7 @@ //! The primary API exposed by this module is the `CosmeticFilterCache` struct, which stores //! cosmetic filters and allows them to be queried efficiently at runtime for any which may be //! relevant to a particular page. +//! To build `CosmeticFilterCache`, use `CosmeticFilterCacheBuilder`. use crate::cosmetic_filter_utils::decode_script_with_permission; #[cfg(test)] diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs index 0cb614c3..df04cf2a 100644 --- a/src/cosmetic_filter_cache_builder.rs +++ b/src/cosmetic_filter_cache_builder.rs @@ -1,3 +1,7 @@ +//! Provides API to prepare and serialize cosmetic filter rules to a flatbuffer. +//! To build the struct, use `CosmeticFilterCacheBuilder`. +//! To use the serialized rules, use `CosmeticFilterCache`. + use crate::cosmetic_filter_cache::ProceduralOrActionFilter; use crate::cosmetic_filter_utils::SpecificFilterType; use crate::cosmetic_filter_utils::{encode_script_with_permission, key_from_selector}; @@ -18,6 +22,7 @@ use flatbuffers::WIPOffset; /// Accumulates hostname-specific rules for a single domain before building HostnameSpecificRules /// Note: hide and inject_script are now handled separately at the top level +/// See HostnameSpecificRules declaration for more details. #[derive(Default)] struct HostnameRule { unhide: Vec, @@ -123,7 +128,6 @@ impl CosmeticFilterCacheBuilder { } } - // TODO: review this fn store_hostname_rule(&mut self, rule: CosmeticFilter) { use SpecificFilterType::*; diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 399817ee..8df17b0e 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -1,3 +1,6 @@ +//! Some utility functions for manipulating cosmetic filter rules. +//! Used by `CosmeticFilterCacheBuilder` and `CosmeticFilterCache`. + use crate::resources::PermissionMask; use memchr::memchr as find_char; diff --git a/src/flatbuffers/fb_network_filter.fbs b/src/flatbuffers/fb_network_filter.fbs index b281b31c..824e4e12 100644 --- a/src/flatbuffers/fb_network_filter.fbs +++ b/src/flatbuffers/fb_network_filter.fbs @@ -29,54 +29,80 @@ table NetworkFilterList { filter_map_values: [NetworkFilter] (required); } +// A table to store the most host-specific cosmetic rules. +// Although, the most common kind of rule (see hostname_inject_script_* +// and hostname_hide_*) are stored separately to save memory. table HostnameSpecificRules { + /// Simple hide exception rules, e.g. `example.com#@#.ad`. + /// The content is the rule's CSS selector. unhide: [string]; + + /// Rules to except a scriptlet to inject along with any arguments, e.g. + /// `example.com#@#+js(acis, Number.isNan)`. + /// The content is the contents of the `+js(...)` syntax construct. + /// In practice, these rules are extremely rare in filter lists. uninject_script: [string]; + + /// Procedural filters and/or filters with a [`CosmeticFilterAction`]. + /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. procedural_action: [string]; + + /// Exceptions for procedural filters and/or filters with a [`CosmeticFilterAction`]. + /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. procedural_action_exception: [string]; } +// A table to store cosmetic filter rules (including supported structures). table CosmeticFilters { /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. + /// Stored as a flat_set. simple_class_rules: [string] (required); /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. + /// Stored as a flat_set. simple_id_rules: [string] (required); /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` + /// Stored as a flat_set. misc_generic_selectors: [string] (required); - /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` - /// These are stored as a multi-map from class name to list of selectors + /// Rules that are the CSS selector of an element to be hidden on all sites, starting with a + /// class, e.g. `##.ad image`. + /// Stored as a multi-map `hostname_hash` => `selector` complex_class_rules_index: [string] (required); complex_class_rules_values: [string] (required); - /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` - /// These are stored as a multi-map from id name to list of selectors + /// Rules that are the CSS selector of an element to be hidden on all sites, starting with an + /// id, e.g. `###banner > .text a`. + /// Stored as a multi-map `hostname_hash` => `selector` complex_id_rules_index: [string] (required); complex_id_rules_values: [string] (required); - /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors + /// Simple hostname-specific hide rules, e.g. `example.com##.ad`. + /// Stored as a multi-map `hostname_hash` => `selector`. + /// Doesn't belong to HostnameSpecificRules for performance reasons. hostname_hide_index: [uint64] (required); hostname_hide_values: [string] (required); - /// Hostname-specific script injection filters - multi-map from hostname hash to script data - /// First byte of each script encodes permission bits to avoid separate permissions array + /// Rules with a scriptlet to inject along with any arguments, e.g. + /// `example.com##+js(acis, Number.isNan)`. + /// Stored as a multi-map `hostname_hash` => `script_plus_permission_byte` + /// The content is the contents of the `+js(...)` syntax construct plus + /// last byte stores permission to save memory. + /// Doesn't belong to HostnameSpecificRules for performance reasons. hostname_inject_script_index: [uint64] (required); hostname_inject_script_values: [string] (required); - // Map from hostname(hash) => HostnameSpecificRules using FlatMultiMapBuilder - // Store only one item per domain using FlatMultiMapBuilder (for remaining rule types) + // A map to store the other host-specific cosmetic rules. hostname_index: [uint64] (required); hostname_values: [HostnameSpecificRules] (required); } // A root type containing a serialized Engine. -// Currently it contains only some of engine fields: -// network filters and supporing struct. table Engine { - // Format version. Should be increased when makeing non back-compatible changes. + // Format version. Should be equal to ADBLOCK_RUST_DAT_VERSION or the data + // is considered as incompatible. version: uint32; // Contains several NetworkFilterList matching to different kinds of lists. diff --git a/src/flatbuffers/fb_network_filter_generated.rs b/src/flatbuffers/fb_network_filter_generated.rs index d2179b84..0bb44918 100644 --- a/src/flatbuffers/fb_network_filter_generated.rs +++ b/src/flatbuffers/fb_network_filter_generated.rs @@ -728,6 +728,8 @@ pub mod fb { } } + /// Simple hide exception rules, e.g. `example.com#@#.ad`. + /// The content is the rule's CSS selector. #[inline] pub fn unhide( &self, @@ -741,6 +743,10 @@ pub mod fb { >>(HostnameSpecificRules::VT_UNHIDE, None) } } + /// Rules to except a scriptlet to inject along with any arguments, e.g. + /// `example.com#@#+js(acis, Number.isNan)`. + /// The content is the contents of the `+js(...)` syntax construct. + /// In practice, these rules are extremely rare in filter lists. #[inline] pub fn uninject_script( &self, @@ -754,6 +760,8 @@ pub mod fb { >>(HostnameSpecificRules::VT_UNINJECT_SCRIPT, None) } } + /// Procedural filters and/or filters with a [`CosmeticFilterAction`]. + /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. #[inline] pub fn procedural_action( &self, @@ -767,6 +775,8 @@ pub mod fb { >>(HostnameSpecificRules::VT_PROCEDURAL_ACTION, None) } } + /// Exceptions for procedural filters and/or filters with a [`CosmeticFilterAction`]. + /// Each is a [`ProceduralOrActionFilter`] struct serialized as JSON. #[inline] pub fn procedural_action_exception( &self, @@ -1131,6 +1141,7 @@ pub mod fb { } /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. + /// Stored as a flat_set. #[inline] pub fn simple_class_rules( &self, @@ -1147,6 +1158,7 @@ pub mod fb { } } /// Rules that are just the CSS id of an element to be hidden on all sites, e.g. `###banner`. + /// Stored as a flat_set. #[inline] pub fn simple_id_rules( &self, @@ -1164,6 +1176,7 @@ pub mod fb { } /// Rules that are the CSS selector of an element to be hidden on all sites that do not fit /// into any of the class or id buckets, e.g. `##a[href="https://malware.com"]` + /// Stored as a flat_set. #[inline] pub fn misc_generic_selectors( &self, @@ -1179,8 +1192,9 @@ pub mod fb { .unwrap() } } - /// Complex class rules - CSS selectors starting with a class, e.g. `##.ad image` - /// These are stored as a multi-map from class name to list of selectors + /// Rules that are the CSS selector of an element to be hidden on all sites, starting with a + /// class, e.g. `##.ad image`. + /// Stored as a multi-map `hostname_hash` => `selector` #[inline] pub fn complex_class_rules_index( &self, @@ -1211,8 +1225,9 @@ pub mod fb { .unwrap() } } - /// Complex id rules - CSS selectors starting with an id, e.g. `###banner > .text a` - /// These are stored as a multi-map from id name to list of selectors + /// Rules that are the CSS selector of an element to be hidden on all sites, starting with an + /// id, e.g. `###banner > .text a`. + /// Stored as a multi-map `hostname_hash` => `selector` #[inline] pub fn complex_id_rules_index( &self, @@ -1243,7 +1258,9 @@ pub mod fb { .unwrap() } } - /// Hostname-specific hide filters - multi-map from hostname hash to CSS selectors + /// Simple hostname-specific hide rules, e.g. `example.com##.ad`. + /// Stored as a multi-map `hostname_hash` => `selector`. + /// Doesn't belong to HostnameSpecificRules for performance reasons. #[inline] pub fn hostname_hide_index(&self) -> flatbuffers::Vector<'a, u64> { // Safety: @@ -1273,8 +1290,12 @@ pub mod fb { .unwrap() } } - /// Hostname-specific script injection filters - multi-map from hostname hash to script data - /// First byte of each script encodes permission bits to avoid separate permissions array + /// Rules with a scriptlet to inject along with any arguments, e.g. + /// `example.com##+js(acis, Number.isNan)`. + /// Stored as a multi-map `hostname_hash` => `script_plus_permission_byte` + /// The content is the contents of the `+js(...)` syntax construct plus + /// last byte stores permission to save memory. + /// Doesn't belong to HostnameSpecificRules for performance reasons. #[inline] pub fn hostname_inject_script_index(&self) -> flatbuffers::Vector<'a, u64> { // Safety: From 35f30ff427eb299ba0417fdf357fa0d59f0c841a Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Wed, 27 Aug 2025 02:53:50 +0700 Subject: [PATCH 12/21] add more tests --- src/flatbuffers/containers/flat_map.rs | 9 ++ tests/unit/flatbuffers/containers/flat_map.rs | 96 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tests/unit/flatbuffers/containers/flat_map.rs diff --git a/src/flatbuffers/containers/flat_map.rs b/src/flatbuffers/containers/flat_map.rs index 509eeb2e..aaabd7db 100644 --- a/src/flatbuffers/containers/flat_map.rs +++ b/src/flatbuffers/containers/flat_map.rs @@ -29,6 +29,11 @@ where } } + #[cfg(test)] + pub fn len(&self) -> usize { + self.keys.len() + } + pub fn get(&self, key: I) -> Option<>::Inner> { let index = self.keys.partition_point(|x| *x < key); if index < self.keys.len() && self.keys.get(index) == key { @@ -67,3 +72,7 @@ impl FlatMapBuilder { } } } + +#[cfg(test)] +#[path = "../../../tests/unit/flatbuffers/containers/flat_map.rs"] +mod unit_tests; diff --git a/tests/unit/flatbuffers/containers/flat_map.rs b/tests/unit/flatbuffers/containers/flat_map.rs new file mode 100644 index 00000000..8a6e726c --- /dev/null +++ b/tests/unit/flatbuffers/containers/flat_map.rs @@ -0,0 +1,96 @@ +#[allow(unknown_lints)] +#[allow( + dead_code, + clippy::all, + unused_imports, + unsafe_code, + mismatched_lifetime_syntaxes +)] +#[path = "./test_containers_generated.rs"] +pub mod flat; +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use super::super::*; + use super::flat::fb_test; + + // Helper function to create a Vector from a slice + fn create_vector_u32<'a>( + builder: &'a mut flatbuffers::FlatBufferBuilder, + data: &'a [u32], + ) -> flatbuffers::Vector<'a, u32> { + let vec_offset = builder.create_vector(data); + builder.finish(vec_offset, None); + let buf = builder.finished_data(); + flatbuffers::root::>(buf).unwrap() + } + + #[test] + fn test_empty_map() { + let index: &[u32] = &[]; + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let values = create_vector_u32(&mut builder, &[]); + let map = FlatMapView::new(index, values); + + assert_eq!(map.len(), 0); + assert!(map.get(1).is_none()); + } + + #[test] + fn test_multiple_elements() { + let index: &[u32] = &[1, 2, 4, 6, 100, 102]; + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let values = create_vector_u32(&mut builder, &[10, 20, 30, 40, 50, 60]); + + let map = FlatMapView::new(index, values); + + assert_eq!(map.len(), 6); + + assert_eq!(map.get(2), Some(20)); + assert_eq!(map.get(4), Some(30)); + assert_eq!(map.get(100), Some(50)); + assert_eq!(map.get(102), Some(60)); + assert!(map.get(103).is_none()); + } + + + #[test] + fn test_string_builder() { + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let mut map = HashMap::new(); + map.insert("b", "20"); + map.insert("a", "10"); + map.insert("c", "30"); + let map = FlatMapBuilder::finish(map, &mut builder); + + // Serialize to the test flatbuffer. + let test_map = fb_test::TestStringMap::create( + &mut builder, + &fb_test::TestStringMapArgs { + keys: Some(map.keys), + values: Some(map.values), + }, + ); + let root = fb_test::TestRoot::create( + &mut builder, + &fb_test::TestRootArgs { + test_string_map: Some(test_map), + ..Default::default() + }, + ); + builder.finish(root, None); + + // Load from the serialized test flatbuffer. + let data = builder.finished_data(); + let root = fb_test::root_as_test_root(data).unwrap(); + let flat_map = root.test_string_map().unwrap(); + let map = FlatMapView::new(flat_map.keys(), flat_map.values()); + + assert_eq!(map.get("a").unwrap(), "10"); + assert_eq!(map.get("b").unwrap(), "20"); + assert_eq!(map.get("c").unwrap(), "30"); + assert!(map.get("d").is_none()); + assert!(map.get("").is_none()); + } +} From 4ce4fb0447b92ca91aa91c396a480aa4a51bedaa Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Wed, 27 Aug 2025 03:08:34 +0700 Subject: [PATCH 13/21] cargo fmt --- tests/unit/flatbuffers/containers/flat_map.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/tests/unit/flatbuffers/containers/flat_map.rs b/tests/unit/flatbuffers/containers/flat_map.rs index 8a6e726c..80be0cfb 100644 --- a/tests/unit/flatbuffers/containers/flat_map.rs +++ b/tests/unit/flatbuffers/containers/flat_map.rs @@ -17,14 +17,14 @@ mod tests { // Helper function to create a Vector from a slice fn create_vector_u32<'a>( - builder: &'a mut flatbuffers::FlatBufferBuilder, - data: &'a [u32], - ) -> flatbuffers::Vector<'a, u32> { - let vec_offset = builder.create_vector(data); - builder.finish(vec_offset, None); - let buf = builder.finished_data(); - flatbuffers::root::>(buf).unwrap() - } + builder: &'a mut flatbuffers::FlatBufferBuilder, + data: &'a [u32], + ) -> flatbuffers::Vector<'a, u32> { + let vec_offset = builder.create_vector(data); + builder.finish(vec_offset, None); + let buf = builder.finished_data(); + flatbuffers::root::>(buf).unwrap() + } #[test] fn test_empty_map() { @@ -39,21 +39,20 @@ mod tests { #[test] fn test_multiple_elements() { - let index: &[u32] = &[1, 2, 4, 6, 100, 102]; - let mut builder = flatbuffers::FlatBufferBuilder::new(); - let values = create_vector_u32(&mut builder, &[10, 20, 30, 40, 50, 60]); - - let map = FlatMapView::new(index, values); + let index: &[u32] = &[1, 2, 4, 6, 100, 102]; + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let values = create_vector_u32(&mut builder, &[10, 20, 30, 40, 50, 60]); - assert_eq!(map.len(), 6); + let map = FlatMapView::new(index, values); - assert_eq!(map.get(2), Some(20)); - assert_eq!(map.get(4), Some(30)); - assert_eq!(map.get(100), Some(50)); - assert_eq!(map.get(102), Some(60)); - assert!(map.get(103).is_none()); - } + assert_eq!(map.len(), 6); + assert_eq!(map.get(2), Some(20)); + assert_eq!(map.get(4), Some(30)); + assert_eq!(map.get(100), Some(50)); + assert_eq!(map.get(102), Some(60)); + assert!(map.get(103).is_none()); + } #[test] fn test_string_builder() { From 02efd495f9b078662b0579a56464eb706ccfc3a2 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Thu, 11 Sep 2025 12:43:50 +0400 Subject: [PATCH 14/21] Fix doc format in .fbs --- src/flatbuffers/fb_network_filter.fbs | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/flatbuffers/fb_network_filter.fbs b/src/flatbuffers/fb_network_filter.fbs index 824e4e12..960450de 100644 --- a/src/flatbuffers/fb_network_filter.fbs +++ b/src/flatbuffers/fb_network_filter.fbs @@ -9,9 +9,9 @@ namespace fb; table NetworkFilter { mask: uint32; // NetworkFilterMask (network.rs) - // These arrays contain sorted (ascending) indices in the |unique_domains_hashes| - // instead of the hashes themselves. This approach saves memory, as there - // typically aren’t many unique hashes + /// These arrays contain sorted (ascending) indices in the |unique_domains_hashes| + /// instead of the hashes themselves. This approach saves memory, as there + /// typically aren’t many unique hashes opt_domains: [uint32]; opt_not_domains: [uint32]; @@ -29,9 +29,9 @@ table NetworkFilterList { filter_map_values: [NetworkFilter] (required); } -// A table to store the most host-specific cosmetic rules. -// Although, the most common kind of rule (see hostname_inject_script_* -// and hostname_hide_*) are stored separately to save memory. +/// A table to store the most host-specific cosmetic rules. +/// Although, the most common kind of rule (see hostname_inject_script_* +/// and hostname_hide_*) are stored separately to save memory. table HostnameSpecificRules { /// Simple hide exception rules, e.g. `example.com#@#.ad`. /// The content is the rule's CSS selector. @@ -52,7 +52,7 @@ table HostnameSpecificRules { procedural_action_exception: [string]; } -// A table to store cosmetic filter rules (including supported structures). +/// A table to store cosmetic filter rules (including supported structures). table CosmeticFilters { /// Rules that are just the CSS class of an element to be hidden on all sites, e.g. `##.ad`. /// Stored as a flat_set. @@ -94,23 +94,23 @@ table CosmeticFilters { hostname_inject_script_index: [uint64] (required); hostname_inject_script_values: [string] (required); - // A map to store the other host-specific cosmetic rules. + /// A map to store the other host-specific cosmetic rules. hostname_index: [uint64] (required); hostname_values: [HostnameSpecificRules] (required); } -// A root type containing a serialized Engine. +/// A root type containing a serialized Engine. table Engine { - // Format version. Should be equal to ADBLOCK_RUST_DAT_VERSION or the data - // is considered as incompatible. + /// Format version. Should be equal to ADBLOCK_RUST_DAT_VERSION or the data + /// is considered as incompatible. version: uint32; - // Contains several NetworkFilterList matching to different kinds of lists. - // The indexes are matching NetworkFilterListId. - // The size must be NetworkFilterListId::Size. + /// Contains several NetworkFilterList matching to different kinds of lists. + /// The indexes are matching NetworkFilterListId. + /// The size must be NetworkFilterListId::Size. network_rules: [NetworkFilterList] (required); - // Contains hashes for opt_(not)_domains. See opt_domains for details. + /// Contains hashes for opt_(not)_domains. See opt_domains for details. unique_domains_hashes: [uint64] (required); cosmetic_filters: CosmeticFilters (required); From 07888d9d2f6c28c50c33410068efd82040cae287 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Thu, 11 Sep 2025 12:50:28 +0400 Subject: [PATCH 15/21] add filter_data_context.rs --- src/blocker.rs | 2 +- src/cosmetic_filter_cache.rs | 2 +- src/engine.rs | 2 +- src/filters/fb_network.rs | 34 ++---------------------------- src/filters/filter_data_context.rs | 31 +++++++++++++++++++++++++++ src/filters/mod.rs | 1 + src/network_filter_list.rs | 3 ++- 7 files changed, 39 insertions(+), 36 deletions(-) create mode 100644 src/filters/filter_data_context.rs diff --git a/src/blocker.rs b/src/blocker.rs index 26d7cc2f..d83861bf 100644 --- a/src/blocker.rs +++ b/src/blocker.rs @@ -6,8 +6,8 @@ use serde::Serialize; use std::collections::HashSet; use std::ops::DerefMut; -use crate::filters::fb_network::FilterDataContextRef; use crate::filters::fb_network_builder::NetworkFilterListId; +use crate::filters::filter_data_context::FilterDataContextRef; use crate::filters::network::NetworkFilterMaskHelper; use crate::network_filter_list::NetworkFilterList; use crate::regex_manager::{RegexManager, RegexManagerDiscardPolicy}; diff --git a/src/cosmetic_filter_cache.rs b/src/cosmetic_filter_cache.rs index 8af401b2..ca39a8b1 100644 --- a/src/cosmetic_filter_cache.rs +++ b/src/cosmetic_filter_cache.rs @@ -13,7 +13,7 @@ use crate::cosmetic_filter_utils::decode_script_with_permission; #[cfg(test)] use crate::filters::cosmetic::CosmeticFilter; use crate::filters::cosmetic::{CosmeticFilterAction, CosmeticFilterOperator}; -use crate::filters::fb_network::FilterDataContextRef; +use crate::filters::filter_data_context::FilterDataContextRef; use crate::flatbuffers::containers::flat_map::FlatMapView; use crate::flatbuffers::containers::flat_multimap::{FlatMapStringView, FlatMultiMapView}; diff --git a/src/engine.rs b/src/engine.rs index b8867254..ec5c3be4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -5,8 +5,8 @@ use crate::cosmetic_filter_cache::{CosmeticFilterCache, UrlSpecificResources}; use crate::cosmetic_filter_cache_builder::CosmeticFilterCacheBuilder; use crate::filters::cosmetic::CosmeticFilter; use crate::filters::fb_builder::EngineFlatBuilder; -use crate::filters::fb_network::{FilterDataContext, FilterDataContextRef}; use crate::filters::fb_network_builder::NetworkRulesBuilder; +use crate::filters::filter_data_context::{FilterDataContext, FilterDataContextRef}; use crate::filters::network::NetworkFilter; use crate::flatbuffers::containers::flat_serialize::FlatSerialize; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index 5bb72399..a53a7943 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -1,13 +1,11 @@ //! Flatbuffer-compatible versions of [NetworkFilter] and related functionality. -use std::collections::HashMap; - +use crate::filters::filter_data_context::FilterDataContext; use crate::filters::network::{NetworkFilterMask, NetworkFilterMaskHelper, NetworkMatchable}; -use crate::flatbuffers::unsafe_tools::{fb_vector_to_slice, VerifiedFlatbufferMemory}; +use crate::flatbuffers::unsafe_tools::fb_vector_to_slice; use crate::regex_manager::RegexManager; use crate::request::Request; -use crate::utils::Hash; #[allow(unknown_lints)] #[allow( @@ -74,34 +72,6 @@ impl ExactSizeIterator for FlatPatternsIterator<'_> { } } -#[cfg(feature = "single-thread")] -pub(crate) type FilterDataContextRef = std::rc::Rc; -#[cfg(not(feature = "single-thread"))] -pub(crate) type FilterDataContextRef = std::sync::Arc; - -// The struct is used to store the flatbuffer and supporting data -// for both network filter and cosmetic filters. -// Supposed to be stored via FilterDataContextRef to avoid copying the data. -pub(crate) struct FilterDataContext { - pub(crate) memory: VerifiedFlatbufferMemory, - pub(crate) unique_domains_hashes_map: HashMap, -} - -impl FilterDataContext { - pub(crate) fn new(memory: VerifiedFlatbufferMemory) -> FilterDataContextRef { - // Reconstruct the unique_domains_hashes_map from the flatbuffer data - let root = memory.root(); - let mut unique_domains_hashes_map: HashMap = HashMap::new(); - for (index, hash) in root.unique_domains_hashes().iter().enumerate() { - unique_domains_hashes_map.insert(hash, index as u32); - } - FilterDataContextRef::new(Self { - memory, - unique_domains_hashes_map, - }) - } -} - /// Internal implementation of [NetworkFilter] that is compatible with flatbuffers. pub(crate) struct FlatNetworkFilter<'a> { key: u64, diff --git a/src/filters/filter_data_context.rs b/src/filters/filter_data_context.rs new file mode 100644 index 00000000..985ef1eb --- /dev/null +++ b/src/filters/filter_data_context.rs @@ -0,0 +1,31 @@ +use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; +use crate::utils::Hash; +use std::collections::HashMap; + +#[cfg(feature = "single-thread")] +pub(crate) type FilterDataContextRef = std::rc::Rc; +#[cfg(not(feature = "single-thread"))] +pub(crate) type FilterDataContextRef = std::sync::Arc; + +// The struct is used to store the flatbuffer and supporting data +// for both network filter and cosmetic filters. +// Supposed to be stored via FilterDataContextRef to avoid copying the data. +pub(crate) struct FilterDataContext { + pub(crate) memory: VerifiedFlatbufferMemory, + pub(crate) unique_domains_hashes_map: HashMap, +} + +impl FilterDataContext { + pub(crate) fn new(memory: VerifiedFlatbufferMemory) -> FilterDataContextRef { + // Reconstruct the unique_domains_hashes_map from the flatbuffer data + let root = memory.root(); + let mut unique_domains_hashes_map: HashMap = HashMap::new(); + for (index, hash) in root.unique_domains_hashes().iter().enumerate() { + unique_domains_hashes_map.insert(hash, index as u32); + } + FilterDataContextRef::new(Self { + memory, + unique_domains_hashes_map, + }) + } +} diff --git a/src/filters/mod.rs b/src/filters/mod.rs index 976377c9..8b0ddb8d 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -7,4 +7,5 @@ pub mod cosmetic; pub(crate) mod fb_builder; pub(crate) mod fb_network; pub(crate) mod fb_network_builder; +pub(crate) mod filter_data_context; pub mod network; diff --git a/src/network_filter_list.rs b/src/network_filter_list.rs index 80b30685..1c72eb33 100644 --- a/src/network_filter_list.rs +++ b/src/network_filter_list.rs @@ -5,7 +5,8 @@ use std::{collections::HashMap, collections::HashSet, fmt}; use flatbuffers::ForwardsUOffset; use crate::filters::fb_network::flat::fb; -use crate::filters::fb_network::{FilterDataContext, FlatNetworkFilter}; +use crate::filters::fb_network::FlatNetworkFilter; +use crate::filters::filter_data_context::FilterDataContext; use crate::filters::network::{ NetworkFilter, NetworkFilterMask, NetworkFilterMaskHelper, NetworkMatchable, }; From c08f3f151700c12e06a8a185eba7c700fdf79746 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Thu, 11 Sep 2025 14:01:41 +0400 Subject: [PATCH 16/21] Add hash to serialized data --- src/engine.rs | 28 +++++++++++++++++++++++++--- src/flatbuffers/unsafe_tools.rs | 16 ++++++++++------ tests/unit/engine.rs | 2 +- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index ec5c3be4..a5a166e5 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -58,6 +58,7 @@ pub struct Engine { } const ADBLOCK_FLATBUFFER_VERSION: u32 = 1; +const HASH_PREFIX_LENGTH: usize = 8; #[derive(Debug)] pub enum DeserializationError { @@ -260,8 +261,13 @@ impl Engine { } /// Serializes the `Engine` into a binary format so that it can be quickly reloaded later. - pub fn serialize(&self) -> &[u8] { - self.filter_data_context.memory.data() + pub fn serialize(&self) -> Vec { + let data = self.filter_data_context.memory.data(); + let mut serialized = Vec::with_capacity(data.len() + HASH_PREFIX_LENGTH); + let hash = seahash::hash(data).to_le_bytes(); + serialized.extend_from_slice(&hash); + serialized.extend_from_slice(data); + serialized } /// Deserialize the `Engine` from the binary format generated by `Engine::serialize`. @@ -271,8 +277,24 @@ impl Engine { /// of adblock-rust; the format is provided only as a caching optimization. pub fn deserialize(&mut self, serialized: &[u8]) -> Result<(), DeserializationError> { let current_tags = self.blocker.tags_enabled(); - let memory = VerifiedFlatbufferMemory::from_raw(serialized.to_vec()) + + let serialized_flat_buffer = &serialized[HASH_PREFIX_LENGTH..]; + + // Check the hash to ensure the data isn't corrupted. + let expected_hash = &serialized[0..HASH_PREFIX_LENGTH]; + let actual_hash = seahash::hash(serialized_flat_buffer).to_le_bytes(); + assert!(expected_hash.len() == HASH_PREFIX_LENGTH); + if expected_hash != actual_hash { + println!( + "Expected hash: {:?}, actual hash: {:?}", + expected_hash, actual_hash + ); + return Err(DeserializationError::ValidationError); + } + + let memory = VerifiedFlatbufferMemory::from_raw(serialized_flat_buffer) .map_err(DeserializationError::FlatBufferParsingError)?; + if memory.root().version() != ADBLOCK_FLATBUFFER_VERSION { return Err(DeserializationError::VersionMismatch( memory.root().version(), diff --git a/src/flatbuffers/unsafe_tools.rs b/src/flatbuffers/unsafe_tools.rs index 15156546..ab9d4fa2 100644 --- a/src/flatbuffers/unsafe_tools.rs +++ b/src/flatbuffers/unsafe_tools.rs @@ -48,8 +48,8 @@ pub(crate) struct VerifiedFlatbufferMemory { } impl VerifiedFlatbufferMemory { - pub(crate) fn from_raw(data: Vec) -> Result { - let memory = Self::from_vec(data); + pub(crate) fn from_raw(data: &[u8]) -> Result { + let memory = Self::from_slice(data); // Verify that the data is a valid flatbuffer. let _ = fb::root_as_engine(memory.data())?; @@ -60,23 +60,27 @@ impl VerifiedFlatbufferMemory { // Creates a new VerifiedFlatbufferMemory from a builder. // Skip the verification, the builder must contains a valid FilterList. pub(crate) fn from_builder(builder: &flatbuffers::FlatBufferBuilder<'_>) -> Self { - let raw_data = builder.finished_data().to_vec(); - Self::from_vec(raw_data) + Self::from_slice(builder.finished_data()) } // Properly align the buffer to MIN_ALIGNMENT bytes. - pub(crate) fn from_vec(mut vec: Vec) -> Self { + pub(crate) fn from_slice(data: &[u8]) -> Self { + let mut vec = Vec::with_capacity(data.len() + MIN_ALIGNMENT); let shift = vec.as_ptr() as usize % MIN_ALIGNMENT; + let start = if shift == 0 { 0 } else { - vec.reserve(vec.len() + MIN_ALIGNMENT); // vec.as_ptr() is changed let shift = vec.as_ptr() as usize % MIN_ALIGNMENT; let padding = MIN_ALIGNMENT - shift; + assert!(vec.capacity() >= padding); vec.splice(0..0, vec![0u8; padding]); padding }; + vec.extend_from_slice(data); + assert!((vec.as_ptr() as usize + start) % MIN_ALIGNMENT == 0); + let memory = Self { raw_data: vec, start, diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index a2560d09..aac654a8 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -156,7 +156,7 @@ mod tests { let serialized = engine.serialize(); let mut deserialized_engine = Engine::default(); deserialized_engine.enable_tags(&["stuff"]); - deserialized_engine.deserialize(serialized).unwrap(); + deserialized_engine.deserialize(&serialized).unwrap(); url_results.into_iter().for_each(|(url, expected_result)| { let request = Request::new(url, "", "").unwrap(); From 7fd35dd3986f060f7cc37bfb6bd07c56f1055d2a Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Thu, 11 Sep 2025 14:24:20 +0400 Subject: [PATCH 17/21] update hashes in tests --- tests/unit/engine.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index aac654a8..1452a17a 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -183,7 +183,7 @@ mod tests { fn deserialization_generate_simple() { let mut engine = Engine::from_rules(["ad-banner"], Default::default()); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 10824009878088122438; + const EXPECTED_HASH: u64 = 18278341446070118707; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -193,7 +193,7 @@ mod tests { let mut engine = Engine::from_rules(["ad-banner$tag=abc"], Default::default()); engine.use_tags(&["abc"]); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 14013192499527032437; + const EXPECTED_HASH: u64 = 8186969451068597583; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -219,9 +219,9 @@ mod tests { let data = engine.serialize().to_vec(); let expected_hash: u64 = if cfg!(feature = "css-validation") { - 8016301231738552031 + 15759319380859205322 } else { - 8518366622637220045 + 16991185680449067492 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG); From c60139b9e9bfe25a84896eb325ed48f1268cc698 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Thu, 11 Sep 2025 14:35:10 +0400 Subject: [PATCH 18/21] move mod for generated code --- src/cosmetic_filter_cache_builder.rs | 2 +- src/filters/fb_builder.rs | 2 +- src/filters/fb_network.rs | 13 +------------ src/filters/fb_network_builder.rs | 2 +- src/filters/mod.rs | 15 +++++++++++++++ src/flatbuffers/unsafe_tools.rs | 2 +- src/network_filter_list.rs | 2 +- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/cosmetic_filter_cache_builder.rs b/src/cosmetic_filter_cache_builder.rs index df04cf2a..2673107f 100644 --- a/src/cosmetic_filter_cache_builder.rs +++ b/src/cosmetic_filter_cache_builder.rs @@ -6,7 +6,7 @@ use crate::cosmetic_filter_cache::ProceduralOrActionFilter; use crate::cosmetic_filter_utils::SpecificFilterType; use crate::cosmetic_filter_utils::{encode_script_with_permission, key_from_selector}; use crate::filters::cosmetic::{CosmeticFilter, CosmeticFilterMask, CosmeticFilterOperator}; -use crate::filters::fb_network::flat::fb; +use crate::filters::flatbuffer_generated::fb; use crate::flatbuffers::containers::flat_map::FlatMapBuilder; use crate::flatbuffers::containers::flat_multimap::FlatMultiMapBuilder; diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 72356b89..4ad8deaf 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -9,7 +9,7 @@ use crate::flatbuffers::containers::flat_serialize::{FlatBuilder, WIPFlatVec}; use crate::flatbuffers::unsafe_tools::VerifiedFlatbufferMemory; use crate::utils::Hash; -use super::fb_network::flat::fb; +use super::flat::fb; #[derive(Default)] pub(crate) struct EngineFlatBuilder<'a> { diff --git a/src/filters/fb_network.rs b/src/filters/fb_network.rs index a53a7943..4d5aebee 100644 --- a/src/filters/fb_network.rs +++ b/src/filters/fb_network.rs @@ -7,18 +7,7 @@ use crate::flatbuffers::unsafe_tools::fb_vector_to_slice; use crate::regex_manager::RegexManager; use crate::request::Request; -#[allow(unknown_lints)] -#[allow( - dead_code, - clippy::all, - unused_imports, - unsafe_code, - mismatched_lifetime_syntaxes -)] -#[path = "../flatbuffers/fb_network_filter_generated.rs"] -pub mod flat; -use flat::fb; - +use crate::filters::flatbuffer_generated::fb; /// A list of string parts that can be matched against a URL. pub(crate) struct FlatPatterns<'a> { patterns: Option>>, diff --git a/src/filters/fb_network_builder.rs b/src/filters/fb_network_builder.rs index fb1d0360..63e0f52a 100644 --- a/src/filters/fb_network_builder.rs +++ b/src/filters/fb_network_builder.rs @@ -14,7 +14,7 @@ use crate::network_filter_list::token_histogram; use crate::optimizer; use crate::utils::{to_short_hash, Hash, ShortHash}; -use super::fb_network::flat::fb; +use super::flat::fb; pub(crate) enum NetworkFilterListId { Csp = 0, diff --git a/src/filters/mod.rs b/src/filters/mod.rs index 8b0ddb8d..9d3394f8 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -9,3 +9,18 @@ pub(crate) mod fb_network; pub(crate) mod fb_network_builder; pub(crate) mod filter_data_context; pub mod network; + +#[allow(unknown_lints)] +#[allow( + dead_code, + clippy::all, + unused_imports, + unsafe_code, + mismatched_lifetime_syntaxes +)] +#[path = "../flatbuffers/fb_network_filter_generated.rs"] +mod flat; + +pub(crate) mod flatbuffer_generated { + pub use super::flat::fb; +} diff --git a/src/flatbuffers/unsafe_tools.rs b/src/flatbuffers/unsafe_tools.rs index ab9d4fa2..3ce79137 100644 --- a/src/flatbuffers/unsafe_tools.rs +++ b/src/flatbuffers/unsafe_tools.rs @@ -1,6 +1,6 @@ //! Unsafe utility functions for working with flatbuffers and other low-level operations. -use crate::filters::fb_network::flat::fb; +use crate::filters::flatbuffer_generated::fb; // Minimum alignment for the beginning of the flatbuffer data. const MIN_ALIGNMENT: usize = 8; diff --git a/src/network_filter_list.rs b/src/network_filter_list.rs index 1c72eb33..2b81ba4d 100644 --- a/src/network_filter_list.rs +++ b/src/network_filter_list.rs @@ -4,9 +4,9 @@ use std::{collections::HashMap, collections::HashSet, fmt}; use flatbuffers::ForwardsUOffset; -use crate::filters::fb_network::flat::fb; use crate::filters::fb_network::FlatNetworkFilter; use crate::filters::filter_data_context::FilterDataContext; +use crate::filters::flatbuffer_generated::fb; use crate::filters::network::{ NetworkFilter, NetworkFilterMask, NetworkFilterMaskHelper, NetworkMatchable, }; From 93bb4571b1c73d4350af0882e48824fa11a0564c Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 15 Sep 2025 16:29:03 +0400 Subject: [PATCH 19/21] return ADBLOCK_RUST_DAT_MAGIC --- src/data_format/mod.rs | 94 ++++++++++++++++++++++++++++++++++++++++++ src/engine.rs | 32 ++------------ src/lib.rs | 1 + tests/unit/engine.rs | 8 ++-- 4 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 src/data_format/mod.rs diff --git a/src/data_format/mod.rs b/src/data_format/mod.rs new file mode 100644 index 00000000..ed0dbbea --- /dev/null +++ b/src/data_format/mod.rs @@ -0,0 +1,94 @@ +//! Allows serialization of the adblock engine into a compact binary format, as well as subsequent +//! rapid deserialization back into an engine. +//! +//! In order to support multiple format versions simultaneously, this module wraps around different +//! serialization/deserialization implementations and can automatically dispatch to the appropriate +//! one. +//! +//! The current .dat file format: +//! 1. magic (4 bytes) +//! 2. seahash of the data (8 bytes) +//! 3. data (the rest of the file) + +/// Newer formats start with this magic byte sequence. +/// Calculated as the leading 4 bytes of `echo -n 'brave/adblock-rust' | sha512sum`. +const ADBLOCK_RUST_DAT_MAGIC: [u8; 4] = [0xd1, 0xd9, 0x3a, 0xaf]; + +const HEADER_PREFIX_LENGTH: usize = 12; + +#[derive(Debug, PartialEq)] +pub enum DeserializationError { + BadHeader, + BadChecksum, + VersionMismatch(u32), + FlatBufferParsingError(flatbuffers::InvalidFlatbuffer), + ValidationError, +} + +pub(crate) fn serialize_dat_file(data: &[u8]) -> Vec { + let mut serialized = Vec::with_capacity(data.len() + HEADER_PREFIX_LENGTH); + let hash = seahash::hash(data).to_le_bytes(); + serialized.extend_from_slice(&ADBLOCK_RUST_DAT_MAGIC); + serialized.extend_from_slice(&hash); + assert_eq!(serialized.len(), HEADER_PREFIX_LENGTH); + + serialized.extend_from_slice(data); + serialized +} + +pub(crate) fn deserialize_dat_file(serialized: &[u8]) -> Result<&[u8], DeserializationError> { + if serialized.len() < HEADER_PREFIX_LENGTH || !serialized.starts_with(&ADBLOCK_RUST_DAT_MAGIC) { + return Err(DeserializationError::BadHeader); + } + let data = &serialized[HEADER_PREFIX_LENGTH..]; + + // Check the hash to ensure the data isn't corrupted. + let expected_hash = &serialized[ADBLOCK_RUST_DAT_MAGIC.len()..HEADER_PREFIX_LENGTH]; + if expected_hash != seahash::hash(data).to_le_bytes() { + println!( + "Expected hash: {:?}, actual hash: {:?}", + expected_hash, + seahash::hash(data).to_le_bytes() + ); + return Err(DeserializationError::BadChecksum); + } + Ok(data) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_magic_bytes() { + use sha2::Digest; + + let mut hasher = sha2::Sha512::new(); + + hasher.update("brave/adblock-rust"); + + let result = hasher.finalize(); + + assert!(result.starts_with(&ADBLOCK_RUST_DAT_MAGIC)); + } + + #[test] + fn serialize_deserialize_test() { + let data = b"test"; + let serialized = serialize_dat_file(data); + let deserialized = deserialize_dat_file(&serialized).unwrap(); + assert_eq!(data, deserialized); + } + + #[test] + fn corrupted_data_test() { + let data = b"test"; + let serialized = serialize_dat_file(data); + let mut corrupted_serialized = serialized.clone(); + corrupted_serialized[HEADER_PREFIX_LENGTH] = 0; + assert_eq!( + Err(DeserializationError::BadChecksum), + deserialize_dat_file(&corrupted_serialized) + ); + } +} diff --git a/src/engine.rs b/src/engine.rs index a5a166e5..774cea6f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -3,6 +3,7 @@ use crate::blocker::{Blocker, BlockerResult}; use crate::cosmetic_filter_cache::{CosmeticFilterCache, UrlSpecificResources}; use crate::cosmetic_filter_cache_builder::CosmeticFilterCacheBuilder; +use crate::data_format::{deserialize_dat_file, serialize_dat_file, DeserializationError}; use crate::filters::cosmetic::CosmeticFilter; use crate::filters::fb_builder::EngineFlatBuilder; use crate::filters::fb_network_builder::NetworkRulesBuilder; @@ -58,14 +59,6 @@ pub struct Engine { } const ADBLOCK_FLATBUFFER_VERSION: u32 = 1; -const HASH_PREFIX_LENGTH: usize = 8; - -#[derive(Debug)] -pub enum DeserializationError { - VersionMismatch(u32), - FlatBufferParsingError(flatbuffers::InvalidFlatbuffer), - ValidationError, -} impl Default for Engine { fn default() -> Self { @@ -263,11 +256,7 @@ impl Engine { /// Serializes the `Engine` into a binary format so that it can be quickly reloaded later. pub fn serialize(&self) -> Vec { let data = self.filter_data_context.memory.data(); - let mut serialized = Vec::with_capacity(data.len() + HASH_PREFIX_LENGTH); - let hash = seahash::hash(data).to_le_bytes(); - serialized.extend_from_slice(&hash); - serialized.extend_from_slice(data); - serialized + serialize_dat_file(data) } /// Deserialize the `Engine` from the binary format generated by `Engine::serialize`. @@ -278,21 +267,8 @@ impl Engine { pub fn deserialize(&mut self, serialized: &[u8]) -> Result<(), DeserializationError> { let current_tags = self.blocker.tags_enabled(); - let serialized_flat_buffer = &serialized[HASH_PREFIX_LENGTH..]; - - // Check the hash to ensure the data isn't corrupted. - let expected_hash = &serialized[0..HASH_PREFIX_LENGTH]; - let actual_hash = seahash::hash(serialized_flat_buffer).to_le_bytes(); - assert!(expected_hash.len() == HASH_PREFIX_LENGTH); - if expected_hash != actual_hash { - println!( - "Expected hash: {:?}, actual hash: {:?}", - expected_hash, actual_hash - ); - return Err(DeserializationError::ValidationError); - } - - let memory = VerifiedFlatbufferMemory::from_raw(serialized_flat_buffer) + let data = deserialize_dat_file(serialized)?; + let memory = VerifiedFlatbufferMemory::from_raw(data) .map_err(DeserializationError::FlatBufferParsingError)?; if memory.root().version() != ADBLOCK_FLATBUFFER_VERSION { diff --git a/src/lib.rs b/src/lib.rs index df02fa99..2e9c84fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ pub mod content_blocking; pub mod cosmetic_filter_cache; mod cosmetic_filter_cache_builder; mod cosmetic_filter_utils; +mod data_format; mod engine; pub mod filters; mod flatbuffers; diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 1452a17a..55f05d9b 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -183,7 +183,7 @@ mod tests { fn deserialization_generate_simple() { let mut engine = Engine::from_rules(["ad-banner"], Default::default()); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 18278341446070118707; + const EXPECTED_HASH: u64 = 9929222687842256015; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -193,7 +193,7 @@ mod tests { let mut engine = Engine::from_rules(["ad-banner$tag=abc"], Default::default()); engine.use_tags(&["abc"]); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 8186969451068597583; + const EXPECTED_HASH: u64 = 8664849823042889453; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -219,9 +219,9 @@ mod tests { let data = engine.serialize().to_vec(); let expected_hash: u64 = if cfg!(feature = "css-validation") { - 15759319380859205322 + 3163499424034456898 } else { - 16991185680449067492 + 4025003425292179107 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG); From 26af341ba4600f2f0a91eed24638eefe5507fab1 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Wed, 17 Sep 2025 01:57:36 +0400 Subject: [PATCH 20/21] Move version to .dat header --- src/data_format/mod.rs | 22 +++++++--- src/engine.rs | 9 +--- src/filters/fb_builder.rs | 2 - src/flatbuffers/fb_network_filter.fbs | 4 -- .../fb_network_filter_generated.rs | 42 +++++++------------ tests/unit/engine.rs | 8 ++-- 6 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/data_format/mod.rs b/src/data_format/mod.rs index ed0dbbea..07eaf7a3 100644 --- a/src/data_format/mod.rs +++ b/src/data_format/mod.rs @@ -7,20 +7,26 @@ //! //! The current .dat file format: //! 1. magic (4 bytes) -//! 2. seahash of the data (8 bytes) -//! 3. data (the rest of the file) +//! 2. version (1 byte) +//! 3. seahash of the data (8 bytes) +//! 4. data (the rest of the file) /// Newer formats start with this magic byte sequence. /// Calculated as the leading 4 bytes of `echo -n 'brave/adblock-rust' | sha512sum`. const ADBLOCK_RUST_DAT_MAGIC: [u8; 4] = [0xd1, 0xd9, 0x3a, 0xaf]; -const HEADER_PREFIX_LENGTH: usize = 12; +/// The version of the data format. +/// If the data format version is incremented, the data is considered as incompatible. +const ADBLOCK_FLATBUFFER_VERSION: u8 = 1; + +/// The total length of the header prefix (magic + version + seahash) +const HEADER_PREFIX_LENGTH: usize = 4 + 1 + 8; #[derive(Debug, PartialEq)] pub enum DeserializationError { BadHeader, BadChecksum, - VersionMismatch(u32), + VersionMismatch(u8), FlatBufferParsingError(flatbuffers::InvalidFlatbuffer), ValidationError, } @@ -29,6 +35,7 @@ pub(crate) fn serialize_dat_file(data: &[u8]) -> Vec { let mut serialized = Vec::with_capacity(data.len() + HEADER_PREFIX_LENGTH); let hash = seahash::hash(data).to_le_bytes(); serialized.extend_from_slice(&ADBLOCK_RUST_DAT_MAGIC); + serialized.push(ADBLOCK_FLATBUFFER_VERSION); serialized.extend_from_slice(&hash); assert_eq!(serialized.len(), HEADER_PREFIX_LENGTH); @@ -40,10 +47,15 @@ pub(crate) fn deserialize_dat_file(serialized: &[u8]) -> Result<&[u8], Deseriali if serialized.len() < HEADER_PREFIX_LENGTH || !serialized.starts_with(&ADBLOCK_RUST_DAT_MAGIC) { return Err(DeserializationError::BadHeader); } + + let version = serialized[ADBLOCK_RUST_DAT_MAGIC.len()]; + if version != ADBLOCK_FLATBUFFER_VERSION { + return Err(DeserializationError::VersionMismatch(version)); + } let data = &serialized[HEADER_PREFIX_LENGTH..]; // Check the hash to ensure the data isn't corrupted. - let expected_hash = &serialized[ADBLOCK_RUST_DAT_MAGIC.len()..HEADER_PREFIX_LENGTH]; + let expected_hash = &serialized[(ADBLOCK_RUST_DAT_MAGIC.len() + 1)..HEADER_PREFIX_LENGTH]; if expected_hash != seahash::hash(data).to_le_bytes() { println!( "Expected hash: {:?}, actual hash: {:?}", diff --git a/src/engine.rs b/src/engine.rs index 774cea6f..bc99908b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -58,8 +58,6 @@ pub struct Engine { filter_data_context: FilterDataContextRef, } -const ADBLOCK_FLATBUFFER_VERSION: u32 = 1; - impl Default for Engine { fn default() -> Self { Self::from_filter_set(FilterSet::new(false), false) @@ -271,11 +269,6 @@ impl Engine { let memory = VerifiedFlatbufferMemory::from_raw(data) .map_err(DeserializationError::FlatBufferParsingError)?; - if memory.root().version() != ADBLOCK_FLATBUFFER_VERSION { - return Err(DeserializationError::VersionMismatch( - memory.root().version(), - )); - } let context = FilterDataContext::new(memory); self.filter_data_context = context; self.blocker = @@ -309,7 +302,7 @@ fn make_flatbuffer( let network_rules = FlatSerialize::serialize(network_rules_builder, &mut builder); let cosmetic_rules = CosmeticFilterCacheBuilder::from_rules(cosmetic_filters); let cosmetic_rules = FlatSerialize::serialize(cosmetic_rules, &mut builder); - builder.finish(network_rules, cosmetic_rules, ADBLOCK_FLATBUFFER_VERSION) + builder.finish(network_rules, cosmetic_rules) } #[cfg(test)] diff --git a/src/filters/fb_builder.rs b/src/filters/fb_builder.rs index 4ad8deaf..6ea470dd 100644 --- a/src/filters/fb_builder.rs +++ b/src/filters/fb_builder.rs @@ -33,14 +33,12 @@ impl<'a> EngineFlatBuilder<'a> { &mut self, network_rules: WIPFlatVec<'a, NetworkFilterListBuilder, EngineFlatBuilder<'a>>, cosmetic_rules: WIPOffset>, - version: u32, ) -> VerifiedFlatbufferMemory { let unique_domains_hashes = Some(self.fb_builder.create_vector(&self.unique_domains_hashes)); let engine = fb::Engine::create( self.raw_builder(), &fb::EngineArgs { - version, network_rules: Some(network_rules), unique_domains_hashes, cosmetic_filters: Some(cosmetic_rules), diff --git a/src/flatbuffers/fb_network_filter.fbs b/src/flatbuffers/fb_network_filter.fbs index 960450de..c85698c0 100644 --- a/src/flatbuffers/fb_network_filter.fbs +++ b/src/flatbuffers/fb_network_filter.fbs @@ -101,10 +101,6 @@ table CosmeticFilters { /// A root type containing a serialized Engine. table Engine { - /// Format version. Should be equal to ADBLOCK_RUST_DAT_VERSION or the data - /// is considered as incompatible. - version: uint32; - /// Contains several NetworkFilterList matching to different kinds of lists. /// The indexes are matching NetworkFilterListId. /// The size must be NetworkFilterListId::Size. diff --git a/src/flatbuffers/fb_network_filter_generated.rs b/src/flatbuffers/fb_network_filter_generated.rs index 0bb44918..35e98f26 100644 --- a/src/flatbuffers/fb_network_filter_generated.rs +++ b/src/flatbuffers/fb_network_filter_generated.rs @@ -118,6 +118,9 @@ pub mod fb { .unwrap() } } + /// These arrays contain sorted (ascending) indices in the |unique_domains_hashes| + /// instead of the hashes themselves. This approach saves memory, as there + /// typically aren’t many unique hashes #[inline] pub fn opt_domains(&self) -> Option> { // Safety: @@ -657,6 +660,9 @@ pub mod fb { pub enum HostnameSpecificRulesOffset {} #[derive(Copy, Clone, PartialEq)] + /// A table to store the most host-specific cosmetic rules. + /// Although, the most common kind of rule (see hostname_inject_script_* + /// and hostname_hide_*) are stored separately to save memory. pub struct HostnameSpecificRules<'a> { pub _tab: flatbuffers::Table<'a>, } @@ -984,6 +990,7 @@ pub mod fb { pub enum CosmeticFiltersOffset {} #[derive(Copy, Clone, PartialEq)] + /// A table to store cosmetic filter rules (including supported structures). pub struct CosmeticFilters<'a> { pub _tab: flatbuffers::Table<'a>, } @@ -1325,6 +1332,7 @@ pub mod fb { .unwrap() } } + /// A map to store the other host-specific cosmetic rules. #[inline] pub fn hostname_index(&self) -> flatbuffers::Vector<'a, u64> { // Safety: @@ -1885,6 +1893,7 @@ pub mod fb { pub enum EngineOffset {} #[derive(Copy, Clone, PartialEq)] + /// A root type containing a serialized Engine. pub struct Engine<'a> { pub _tab: flatbuffers::Table<'a>, } @@ -1900,10 +1909,9 @@ pub mod fb { } impl<'a> Engine<'a> { - pub const VT_VERSION: flatbuffers::VOffsetT = 4; - pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 6; - pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 8; - pub const VT_COSMETIC_FILTERS: flatbuffers::VOffsetT = 10; + pub const VT_NETWORK_RULES: flatbuffers::VOffsetT = 4; + pub const VT_UNIQUE_DOMAINS_HASHES: flatbuffers::VOffsetT = 6; + pub const VT_COSMETIC_FILTERS: flatbuffers::VOffsetT = 8; #[inline] pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { @@ -1929,12 +1937,10 @@ pub mod fb { if let Some(x) = args.network_rules { builder.add_network_rules(x); } - builder.add_version(args.version); builder.finish() } pub fn unpack(&self) -> EngineT { - let version = self.version(); let network_rules = { let x = self.network_rules(); x.iter().map(|t| t.unpack()).collect() @@ -1948,20 +1954,15 @@ pub mod fb { Box::new(x.unpack()) }; EngineT { - version, network_rules, unique_domains_hashes, cosmetic_filters, } } - #[inline] - pub fn version(&self) -> u32 { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { self._tab.get::(Engine::VT_VERSION, Some(0)).unwrap() } - } + /// Contains several NetworkFilterList matching to different kinds of lists. + /// The indexes are matching NetworkFilterListId. + /// The size must be NetworkFilterListId::Size. #[inline] pub fn network_rules( &self, @@ -1977,6 +1978,7 @@ pub mod fb { .unwrap() } } + /// Contains hashes for opt_(not)_domains. See opt_domains for details. #[inline] pub fn unique_domains_hashes(&self) -> flatbuffers::Vector<'a, u64> { // Safety: @@ -2015,7 +2017,6 @@ pub mod fb { ) -> Result<(), flatbuffers::InvalidFlatbuffer> { use self::flatbuffers::Verifiable; v.visit_table(pos)? - .visit_field::("version", Self::VT_VERSION, false)? .visit_field::>, >>("network_rules", Self::VT_NETWORK_RULES, true)? @@ -2034,7 +2035,6 @@ pub mod fb { } } pub struct EngineArgs<'a> { - pub version: u32, pub network_rules: Option< flatbuffers::WIPOffset< flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, @@ -2047,7 +2047,6 @@ pub mod fb { #[inline] fn default() -> Self { EngineArgs { - version: 0, network_rules: None, // required field unique_domains_hashes: None, // required field cosmetic_filters: None, // required field @@ -2060,10 +2059,6 @@ pub mod fb { start_: flatbuffers::WIPOffset, } impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EngineBuilder<'a, 'b, A> { - #[inline] - pub fn add_version(&mut self, version: u32) { - self.fbb_.push_slot::(Engine::VT_VERSION, version, 0); - } #[inline] pub fn add_network_rules( &mut self, @@ -2123,7 +2118,6 @@ pub mod fb { impl core::fmt::Debug for Engine<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut ds = f.debug_struct("Engine"); - ds.field("version", &self.version()); ds.field("network_rules", &self.network_rules()); ds.field("unique_domains_hashes", &self.unique_domains_hashes()); ds.field("cosmetic_filters", &self.cosmetic_filters()); @@ -2133,7 +2127,6 @@ pub mod fb { #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct EngineT { - pub version: u32, pub network_rules: Vec, pub unique_domains_hashes: Vec, pub cosmetic_filters: Box, @@ -2141,7 +2134,6 @@ pub mod fb { impl Default for EngineT { fn default() -> Self { Self { - version: 0, network_rules: Default::default(), unique_domains_hashes: Default::default(), cosmetic_filters: Default::default(), @@ -2153,7 +2145,6 @@ pub mod fb { &self, _fbb: &mut flatbuffers::FlatBufferBuilder<'b, A>, ) -> flatbuffers::WIPOffset> { - let version = self.version; let network_rules = Some({ let x = &self.network_rules; let w: Vec<_> = x.iter().map(|t| t.pack(_fbb)).collect(); @@ -2170,7 +2161,6 @@ pub mod fb { Engine::create( _fbb, &EngineArgs { - version, network_rules, unique_domains_hashes, cosmetic_filters, diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 55f05d9b..2accedf3 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -183,7 +183,7 @@ mod tests { fn deserialization_generate_simple() { let mut engine = Engine::from_rules(["ad-banner"], Default::default()); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 9929222687842256015; + const EXPECTED_HASH: u64 = 7625309928997750719; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -193,7 +193,7 @@ mod tests { let mut engine = Engine::from_rules(["ad-banner$tag=abc"], Default::default()); engine.use_tags(&["abc"]); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 8664849823042889453; + const EXPECTED_HASH: u64 = 13319836777873633887; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -219,9 +219,9 @@ mod tests { let data = engine.serialize().to_vec(); let expected_hash: u64 = if cfg!(feature = "css-validation") { - 3163499424034456898 + 4223849921889267051 } else { - 4025003425292179107 + 5368247436179830601 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG); From abd58d9b8ba8eaa10282f7b5cb0af6c64109ce2d Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Wed, 17 Sep 2025 02:21:29 +0400 Subject: [PATCH 21/21] set ADBLOCK_FLATBUFFER_VERSION=2 --- src/data_format/mod.rs | 2 +- tests/unit/engine.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/data_format/mod.rs b/src/data_format/mod.rs index 07eaf7a3..98b980e0 100644 --- a/src/data_format/mod.rs +++ b/src/data_format/mod.rs @@ -17,7 +17,7 @@ const ADBLOCK_RUST_DAT_MAGIC: [u8; 4] = [0xd1, 0xd9, 0x3a, 0xaf]; /// The version of the data format. /// If the data format version is incremented, the data is considered as incompatible. -const ADBLOCK_FLATBUFFER_VERSION: u8 = 1; +const ADBLOCK_FLATBUFFER_VERSION: u8 = 2; /// The total length of the header prefix (magic + version + seahash) const HEADER_PREFIX_LENGTH: usize = 4 + 1 + 8; diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 2accedf3..674384bc 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -183,7 +183,7 @@ mod tests { fn deserialization_generate_simple() { let mut engine = Engine::from_rules(["ad-banner"], Default::default()); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 7625309928997750719; + const EXPECTED_HASH: u64 = 15201305923211912617; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -193,7 +193,7 @@ mod tests { let mut engine = Engine::from_rules(["ad-banner$tag=abc"], Default::default()); engine.use_tags(&["abc"]); let data = engine.serialize().to_vec(); - const EXPECTED_HASH: u64 = 13319836777873633887; + const EXPECTED_HASH: u64 = 5114301339390262037; assert_eq!(hash(&data), EXPECTED_HASH, "{}", HASH_MISMATCH_MSG); engine.deserialize(&data).unwrap(); } @@ -219,9 +219,9 @@ mod tests { let data = engine.serialize().to_vec(); let expected_hash: u64 = if cfg!(feature = "css-validation") { - 4223849921889267051 + 2942520321544562177 } else { - 5368247436179830601 + 17713004238689548675 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG);