Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
dc9d1c1
Refactor is bad filter (0.13.x) (#641)
atuchin-m May 11, 2026
dc80187
Refactor more mask items (0.13.x) (#642)
atuchin-m May 12, 2026
2e8345f
Set mask default to save memory (#639)
atuchin-m May 12, 2026
aaa99d1
[perf, memory] Add single/multi_pattern to optimize memory usage (0.1…
atuchin-m May 12, 2026
a7028ad
add test for default flatbuffer NetworkFilterMask
antonok-edm May 12, 2026
a7b4277
New API for FilterSet & Engine (#652)
atuchin-m May 28, 2026
be9365f
Streaming filter parsing (#653)
atuchin-m Jun 5, 2026
ab6df43
[perf] Use Cow/&str in network filter parsing (#664)
atuchin-m Jun 8, 2026
f18336c
Speedup get_id() (#670)
atuchin-m Jun 11, 2026
508257a
Add HTTP method (HEAD, POST, GET) support to NetworkFilter parsing,
boocmp Jun 5, 2026
36199a0
Added support for HTTP methods (GET, HEAD, POST) in Request and Netwo…
boocmp Jun 5, 2026
53f5024
Extended request API to accept optional HTTP method parameter.
boocmp Jun 5, 2026
e21d6b9
Removed new_with_method fn.
boocmp Jun 5, 2026
cf09132
Rebased.
boocmp Jun 8, 2026
8ec1c78
Compilation.
boocmp Jun 8, 2026
aa1da0b
Updated hash.
boocmp Jun 8, 2026
698c952
Compilation.
boocmp Jun 8, 2026
23021cf
Improved NetworkFilter tests readability.
boocmp Jun 10, 2026
7d6fa42
Added deprecation warning for passing debug as the 4th argument.
boocmp Jun 11, 2026
f6305a3
Renamed FROM_METHODS to FROM_ANY_METHODS.
boocmp Jun 11, 2026
85a8e35
Extended RequestMethod enum to include all HTTP methods.
boocmp Jun 11, 2026
2d4a6da
Using 'get' method and added error handling for generichide method co…
boocmp Jun 11, 2026
9a8466a
Merge pull request #666 from brave/support_method_option
boocmp Jun 12, 2026
7f413a5
Added parser for $to option in NetworkFilter.
boocmp Jun 12, 2026
9a83ae5
Extended NetworkFilter to support additional options: opt_to_domains,…
boocmp Jun 12, 2026
806f1c3
Added hostname and entity hash calculations to Request.
boocmp Jun 12, 2026
34c168a
Extended fb to support of opt_to_domains, opt_not_to_domains, opt_to_…
boocmp Jun 12, 2026
955bade
clippy.
boocmp Jun 12, 2026
b2ccbfb
Perf
boocmp Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions benches/bench_cosmetic_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ mod test_utils;
pub fn make_engine() -> Engine {
use adblock::resources::Resource;

let rules = test_utils::rules_from_lists(&["data/brave/brave-main-list.txt"]);
let rules = test_utils::rules_from_lists(["data/brave/brave-main-list.txt"]);
let resource_json = std::fs::read_to_string("data/brave/brave-resources.json").unwrap();
let resource_list: Vec<Resource> = serde_json::from_str(&resource_json).unwrap();
let mut engine = Engine::from_rules_parametrised(rules, Default::default(), true, true);
let mut engine = Engine::new_with_list_text_parametrised(rules, Default::default(), true, true);
engine.use_resources(resource_list);
engine
}
Expand Down
75 changes: 35 additions & 40 deletions benches/bench_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};

use adblock::request::Request;
use adblock::url_parser::parse_url;
use adblock::{Engine, FilterSet};
use adblock::Engine;

#[path = "../tests/test_utils.rs"]
mod test_utils;
Expand All @@ -20,30 +20,20 @@ struct TestRequest {

impl From<&TestRequest> for Request {
fn from(v: &TestRequest) -> Self {
Request::new(&v.url, &v.frameUrl, &v.cpt).unwrap()
Request::new(&v.url, &v.frameUrl, &v.cpt, "").unwrap()
}
}

fn load_requests() -> Vec<TestRequest> {
let requests_str = rules_from_lists(&["data/requests.json"]);
let requests_str = rules_from_lists(["data/requests.json"]);
let reqs: Vec<TestRequest> = requests_str
.into_iter()
.map(|r| serde_json::from_str(&r))
.lines()
.map(serde_json::from_str)
.filter_map(Result::ok)
.collect();
reqs
}

fn get_engine(rules: impl IntoIterator<Item = impl AsRef<str>>) -> Engine {
let (network_filters, cosmetic_filters) =
adblock::lists::parse_filters(rules, false, Default::default());

Engine::from_filter_set(
FilterSet::new_with_rules(network_filters, cosmetic_filters, false),
true,
)
}

fn bench_rule_matching(engine: &Engine, requests: &[TestRequest]) -> (u32, u32) {
let mut matches = 0;
let mut passes = 0;
Expand Down Expand Up @@ -87,6 +77,7 @@ fn bench_rule_matching_browserlike(blocker: &Engine, requests: &[ParsedRequest])
source_hostname,
request_type,
*third_party,
"",
));
if check.matched {
matches += 1;
Expand All @@ -112,21 +103,21 @@ fn rule_match(c: &mut Criterion) {
group.sample_size(10);

group.bench_function("el+ep", move |b| {
let rules = rules_from_lists(&[
let rules = rules_from_lists([
"data/easylist.to/easylist/easylist.txt",
"data/easylist.to/easylist/easyprivacy.txt",
]);
let engine = Engine::from_rules(rules, Default::default());
let engine = Engine::new_with_list_text(rules, Default::default());
b.iter(|| bench_rule_matching(&engine, &elep_req))
});
group.bench_function("easylist", move |b| {
let rules = rules_from_lists(&["data/easylist.to/easylist/easylist.txt"]);
let engine = Engine::from_rules(rules, Default::default());
let rules = rules_from_lists(["data/easylist.to/easylist/easylist.txt"]);
let engine = Engine::new_with_list_text(rules, Default::default());
b.iter(|| bench_rule_matching(&engine, &el_req))
});
group.bench_function("slimlist", move |b| {
let rules = rules_from_lists(&["data/slim-list.txt"]);
let engine = Engine::from_rules(rules, Default::default());
let rules = rules_from_lists(["data/slim-list.txt"]);
let engine = Engine::new_with_list_text(rules, Default::default());
b.iter(|| bench_rule_matching(&engine, &slim_req))
});

Expand All @@ -136,15 +127,15 @@ fn rule_match(c: &mut Criterion) {
fn rule_match_parsed_el(c: &mut Criterion) {
let mut group = c.benchmark_group("rule-match-parsed");

let rules = rules_from_lists(&["data/easylist.to/easylist/easylist.txt"]);
let rules = rules_from_lists(["data/easylist.to/easylist/easylist.txt"]);
let requests = load_requests();
let requests_parsed: Vec<_> = requests
.into_iter()
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt))
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt, ""))
.filter_map(Result::ok)
.collect();
let requests_len = requests_parsed.len() as u64;
let engine = get_engine(rules);
let engine = Engine::new_with_list_text(rules, Default::default());

group.throughput(Throughput::Elements(requests_len));
group.sample_size(10);
Expand All @@ -159,27 +150,27 @@ fn rule_match_parsed_el(c: &mut Criterion) {
fn rule_match_parsed_elep_slimlist(c: &mut Criterion) {
let mut group = c.benchmark_group("rule-match-parsed");

let full_rules = rules_from_lists(&[
let full_rules = rules_from_lists([
"data/easylist.to/easylist/easylist.txt",
"data/easylist.to/easylist/easyprivacy.txt",
]);
let engine = get_engine(full_rules);
let engine = Engine::new_with_list_text(full_rules, Default::default());

let requests = load_requests();
let requests_parsed: Vec<_> = requests
.into_iter()
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt))
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt, ""))
.filter_map(Result::ok)
.collect();
let requests_len = requests_parsed.len() as u64;

let slim_rules = rules_from_lists(&["data/slim-list.txt"]);
let slim_engine = get_engine(slim_rules);
let slim_rules = rules_from_lists(["data/slim-list.txt"]);
let slim_engine = Engine::new_with_list_text(slim_rules, Default::default());

let requests_copy = load_requests();
let requests_parsed_copy: Vec<_> = requests_copy
.into_iter()
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt))
.map(|r| Request::new(&r.url, &r.frameUrl, &r.cpt, ""))
.filter_map(Result::ok)
.collect();

Expand Down Expand Up @@ -245,26 +236,30 @@ fn rule_match_browserlike_comparable(c: &mut Criterion) {
let requests = requests_parsed(&requests);

group.bench_function("el+ep", |b| {
let rules = rules_from_lists(&[
let rules = rules_from_lists([
"data/easylist.to/easylist/easylist.txt",
"data/easylist.to/easylist/easyprivacy.txt",
]);
let engine = Engine::from_rules_parametrised(rules, Default::default(), false, true);
let engine =
Engine::new_with_list_text_parametrised(rules, Default::default(), false, true);
b.iter(|| bench_rule_matching_browserlike(&engine, &requests))
});
group.bench_function("el", |b| {
let rules = rules_from_lists(&["data/easylist.to/easylist/easylist.txt"]);
let engine = Engine::from_rules_parametrised(rules, Default::default(), false, true);
let rules = rules_from_lists(["data/easylist.to/easylist/easylist.txt"]);
let engine =
Engine::new_with_list_text_parametrised(rules, Default::default(), false, true);
b.iter(|| bench_rule_matching_browserlike(&engine, &requests))
});
group.bench_function("slimlist", |b| {
let rules = rules_from_lists(&["data/slim-list.txt"]);
let engine = Engine::from_rules_parametrised(rules, Default::default(), false, true);
let rules = rules_from_lists(["data/slim-list.txt"]);
let engine =
Engine::new_with_list_text_parametrised(rules, Default::default(), false, true);
b.iter(|| bench_rule_matching_browserlike(&engine, &requests))
});
group.bench_function("brave-list", |b| {
let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]);
let engine = Engine::from_rules_parametrised(rules, Default::default(), false, true);
let rules = rules_from_lists(["data/brave/brave-main-list.txt"]);
let engine =
Engine::new_with_list_text_parametrised(rules, Default::default(), false, true);
b.iter(|| bench_rule_matching_browserlike(&engine, &requests))
});

Expand All @@ -288,9 +283,9 @@ fn rule_match_first_request(c: &mut Criterion) {
b.iter_custom(|iters| {
let mut total_time = std::time::Duration::ZERO;
for _ in 0..iters {
let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]);
let rules = rules_from_lists(["data/brave/brave-main-list.txt"]);
let engine =
Engine::from_rules_parametrised(rules, Default::default(), false, true);
Engine::new_with_list_text_parametrised(rules, Default::default(), false, true);

// Measure only the matching time, skip setup and destruction
let start_time = std::time::Instant::now();
Expand Down
12 changes: 6 additions & 6 deletions benches/bench_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,15 @@ struct TestRequest {

impl From<&TestRequest> for Request {
fn from(v: &TestRequest) -> Self {
Request::new(&v.url, &v.frameUrl, &v.cpt).unwrap()
Request::new(&v.url, &v.frameUrl, &v.cpt, "").unwrap()
}
}

fn load_requests() -> Vec<TestRequest> {
let requests_str = rules_from_lists(&["data/requests.json"]);
let requests_str = rules_from_lists(["data/requests.json"]);
let reqs: Vec<TestRequest> = requests_str
.into_iter()
.map(|r| serde_json::from_str(&r))
.lines()
.map(serde_json::from_str)
.filter_map(Result::ok)
.collect();
reqs
Expand Down Expand Up @@ -243,8 +243,8 @@ fn bench_cb(
) {
let single_run = || {
ALLOCATOR.reset();
let rules = rules_from_lists(&["data/brave/brave-main-list.txt"]);
let mut engine = Engine::from_rules(rules, Default::default());
let rules = rules_from_lists(["data/brave/brave-main-list.txt"]);
let mut engine = Engine::new_with_list_text(rules, Default::default());
let resource_json = std::fs::read_to_string("data/brave/brave-resources.json").unwrap();
let resource_list: Vec<Resource> = serde_json::from_str(&resource_json).unwrap();
std::mem::drop(resource_json);
Expand Down
46 changes: 28 additions & 18 deletions benches/bench_redirect_performance.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use adblock::{Engine, FilterSet};
use std::borrow::Cow;
use std::sync::OnceLock;

use adblock::Engine;
use criterion::*;
use tokio::runtime::Runtime;

use adblock::filters::network::{NetworkFilter, NetworkFilterMask, NetworkFilterMaskHelper};
use adblock::filters::network::{NetworkFilter, NetworkFilterMask};
use adblock::request::Request;
use adblock::resources::Resource;

Expand Down Expand Up @@ -55,23 +58,31 @@ async fn get_all_filters() -> Vec<String> {
.collect()
}

/// Gets all rules with redirects, and modifies them to apply to resources at `a{0-n}.com/bad.js`
fn get_redirect_rules() -> Vec<NetworkFilter> {
let async_runtime = Runtime::new().expect("Could not start Tokio runtime");
static ALL_FILTERS: OnceLock<Box<[String]>> = OnceLock::new();

let filters = async_runtime.block_on(get_all_filters());
let (network_filters, _) = adblock::lists::parse_filters(&filters, true, Default::default());
fn all_filters() -> &'static [String] {
ALL_FILTERS.get_or_init(|| {
let async_runtime = Runtime::new().expect("Could not start Tokio runtime");
async_runtime.block_on(get_all_filters()).into_boxed_slice()
})
}

/// Gets all rules with redirects, and modifies them to apply to resources at `a{0-n}.com/bad.js`
fn get_redirect_rules() -> Vec<NetworkFilter<'static>> {
let filters = all_filters();
let (network_filters, _) =
adblock::lists::parse_filters(filters.iter().map(|s| s.as_str()), true, Default::default());

network_filters
.into_iter()
.filter(NetworkFilter::is_redirect)
.filter(NetworkFilter::also_block_redirect)
.filter(|rule| rule.modifier_option.as_ref().unwrap() != "none")
.filter(|rule| rule.modifier_option.unwrap() != "none")
.enumerate()
.map(|(index, mut rule)| {
rule.mask.insert(NetworkFilterMask::IS_LEFT_ANCHOR);
rule.mask.insert(NetworkFilterMask::IS_RIGHT_ANCHOR);
rule.hostname = Some(format!("a{index}.com/bad.js"));
rule.hostname = Some(Cow::Owned(format!("a{index}.com/bad.js")));

rule.filter = adblock::filters::network::FilterPart::Empty;
rule.mask.remove(NetworkFilterMask::IS_HOSTNAME_ANCHOR);
Expand All @@ -86,8 +97,7 @@ fn get_redirect_rules() -> Vec<NetworkFilter> {

/// Loads the supplied rules, and the test set of resources, into a Engine
fn get_preloaded_engine(rules: Vec<NetworkFilter>) -> Engine {
let filter_set = FilterSet::new_with_rules(rules, vec![], false);
Engine::from_filter_set(filter_set, true /* optimize */)
Engine::new_with_parsed_rules(rules, vec![], true)
}

fn get_resources_for_filters(#[allow(unused)] filters: &[NetworkFilter]) -> Vec<Resource> {
Expand Down Expand Up @@ -118,17 +128,17 @@ fn get_resources_for_filters(#[allow(unused)] filters: &[NetworkFilter]) -> Vec<
.iter()
.filter(|f| f.is_redirect())
.map(|f| {
let mut redirect = f.modifier_option.as_ref().unwrap().as_str();
let mut redirect = f.modifier_option.as_ref().unwrap().to_string();
// strip priority, if present
if let Some(i) = redirect.rfind(':') {
redirect = &redirect[0..i];
redirect.truncate(i);
}

Resource {
name: redirect.to_owned(),
name: redirect.clone(),
aliases: vec![],
kind: ResourceType::Mime(MimeType::from_extension(redirect)),
content: BASE64_STANDARD.encode(redirect),
kind: ResourceType::Mime(MimeType::from_extension(&redirect)),
content: BASE64_STANDARD.encode(&redirect),
dependencies: vec![],
permission: Default::default(),
}
Expand Down Expand Up @@ -175,7 +185,7 @@ pub fn build_custom_requests(rules: Vec<NetworkFilter>) -> Vec<Request> {
let domain = &rule_hostname[..rule_hostname.find('/').unwrap()];
let hostname = domain;

let raw_line = rule.raw_line.clone().unwrap();
let raw_line = rule.raw_line.as_deref().unwrap().to_string();
let source_hostname = if rule.opt_domains.is_some() {
let domain_start = raw_line.rfind("domain=").unwrap() + "domain=".len();
let from_start = &raw_line[domain_start..];
Expand All @@ -194,7 +204,7 @@ pub fn build_custom_requests(rules: Vec<NetworkFilter>) -> Vec<Request> {

let source_url = format!("https://{source_hostname}");

Request::new(&url, &source_url, raw_type).unwrap()
Request::new(&url, &source_url, raw_type, "").unwrap()
})
.collect::<Vec<_>>()
}
Expand Down
Loading
Loading