Skip to content

Commit 52aeee6

Browse files
committed
Added to= option matching.
1 parent dc4d694 commit 52aeee6

13 files changed

Lines changed: 369 additions & 55 deletions

src/data_format/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const ADBLOCK_RUST_DAT_MAGIC: [u8; 4] = [0xd1, 0xd9, 0x3a, 0xaf];
1919

2020
/// The version of the data format.
2121
/// If the data format version is incremented, the data is considered as incompatible.
22-
const ADBLOCK_RUST_DAT_VERSION: u8 = 7;
22+
const ADBLOCK_RUST_DAT_VERSION: u8 = 8;
2323

2424
/// The total length of the header prefix (magic + version + seahash)
2525
const HEADER_PREFIX_LENGTH: usize = 4 + 1 + 8;

src/filters/fb_network.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ impl<'a> FlatNetworkFilter<'a> {
143143
.map(|data| fb_vector_to_slice(data))
144144
}
145145

146+
#[inline(always)]
147+
pub fn include_to_domains(&self) -> Option<&[u32]> {
148+
self.fb_filter
149+
.opt_to_domains()
150+
.map(|data| fb_vector_to_slice(data))
151+
}
152+
153+
#[inline(always)]
154+
pub fn exclude_to_domains(&self) -> Option<&[u32]> {
155+
self.fb_filter
156+
.opt_to_not_domains()
157+
.map(|data| fb_vector_to_slice(data))
158+
}
159+
146160
#[inline(always)]
147161
pub fn hostname(&self) -> Option<&'a str> {
148162
if self.mask.is_hostname_anchor() {
@@ -260,14 +274,28 @@ impl NetworkMatchable for FlatNetworkFilter<'_> {
260274
}
261275
if !check_included_domains_mapped(
262276
self.include_domains(),
263-
request,
277+
request.source_hostname_hashes.as_deref(),
264278
&self.filter_data_context.unique_domains_hashes_map,
265279
) {
266280
return false;
267281
}
268282
if !check_excluded_domains_mapped(
269283
self.exclude_domains(),
270-
request,
284+
request.source_hostname_hashes.as_deref(),
285+
&self.filter_data_context.unique_domains_hashes_map,
286+
) {
287+
return false;
288+
}
289+
if !check_included_domains_mapped(
290+
self.include_to_domains(),
291+
request.hostname_hashes.as_deref(),
292+
&self.filter_data_context.unique_domains_hashes_map,
293+
) {
294+
return false;
295+
}
296+
if !check_excluded_domains_mapped(
297+
self.exclude_to_domains(),
298+
request.hostname_hashes.as_deref(),
271299
&self.filter_data_context.unique_domains_hashes_map,
272300
) {
273301
return false;

src/filters/fb_network_builder.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl Default for NetworkFilterDebugData {
5353
struct NetworkFilterListBuilder<'a, 'f> {
5454
filter_map_builder: FlatMultiMapBuilder<ShortHash, NetworkFilterFlatEntry<'a>>,
5555
opt_domains_map_builder: FlatMultiMapBuilder<ShortHash, NetworkFilterFlatEntry<'a>>,
56+
opt_to_domains_map_builder: FlatMultiMapBuilder<ShortHash, NetworkFilterFlatEntry<'a>>,
5657
token_frequencies: TokenSelector,
5758
filters_to_optimize: HashMap<ShortHash, Vec<NetworkFilter<'f>>>,
5859
tokens_buffer: TokensBuffer,
@@ -173,6 +174,7 @@ impl<'a, 'f> NetworkFilterListBuilder<'a, 'f> {
173174
Self {
174175
filter_map_builder: FlatMultiMapBuilder::with_capacity(1024),
175176
opt_domains_map_builder: FlatMultiMapBuilder::with_capacity(256),
177+
opt_to_domains_map_builder: FlatMultiMapBuilder::with_capacity(256),
176178
token_frequencies: TokenSelector::new(1024),
177179
filters_to_optimize: HashMap::new(),
178180
tokens_buffer: TokensBuffer::default(),
@@ -216,6 +218,12 @@ impl<'a, 'f> NetworkFilterListBuilder<'a, 'f> {
216218
.insert(to_short_hash(*token), NetworkFilterFlatEntry { filter, id });
217219
}
218220
}
221+
FilterTokens::OptToDomains => {
222+
for token in &self.tokens_buffer {
223+
self.opt_to_domains_map_builder
224+
.insert(to_short_hash(*token), NetworkFilterFlatEntry { filter, id });
225+
}
226+
}
219227
}
220228
} else {
221229
// Defer serialization to the optimizer (pattern map only).
@@ -257,11 +265,6 @@ impl<'a, 'f> NetworkRulesBuilder<'a, 'f> {
257265
return;
258266
}
259267

260-
// For now, filters with $to options are parsed but ignored
261-
// to preserve existing matching behavior.
262-
if filter.has_to_option() {
263-
return;
264-
}
265268

266269
// Redirects are independent of blocking behavior.
267270
if filter.is_redirect() {
@@ -354,11 +357,16 @@ impl<'a, 'f> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder<'a
354357
rule_list
355358
.opt_domains_map_builder
356359
.retain_by_value(|entry| !value.bad_filter_ids.contains(&entry.id));
360+
rule_list
361+
.opt_to_domains_map_builder
362+
.retain_by_value(|entry| !value.bad_filter_ids.contains(&entry.id));
357363

358364
let flat_filter_map =
359365
FlatMultiMapBuilder::finish(rule_list.filter_map_builder, builder);
360366
let flat_opt_domains_map =
361367
FlatMultiMapBuilder::finish(rule_list.opt_domains_map_builder, builder);
368+
let flat_opt_to_domains_map =
369+
FlatMultiMapBuilder::finish(rule_list.opt_to_domains_map_builder, builder);
362370

363371
serialized_lists.push(fb::NetworkFilterList::create(
364372
builder.raw_builder(),
@@ -367,6 +375,8 @@ impl<'a, 'f> FlatSerialize<'a, EngineFlatBuilder<'a>> for NetworkRulesBuilder<'a
367375
filter_map_values: Some(flat_filter_map.values),
368376
opt_domains_map_index: Some(flat_opt_domains_map.keys),
369377
opt_domains_map_values: Some(flat_opt_domains_map.values),
378+
opt_to_domains_map_index: Some(flat_opt_to_domains_map.keys),
379+
opt_to_domains_map_values: Some(flat_opt_to_domains_map.values),
370380
},
371381
));
372382
}

src/filters/network.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ pub enum FilterPart<'a> {
333333
pub(crate) enum FilterTokens {
334334
Empty,
335335
OptDomains,
336+
OptToDomains,
336337
Other,
337338
}
338339

@@ -974,6 +975,8 @@ impl<'a> NetworkFilter<'a> {
974975
self.hostname.as_deref(),
975976
self.opt_domains.as_ref(),
976977
self.opt_not_domains.as_ref(),
978+
self.opt_to_domains.as_ref(),
979+
self.opt_to_not_domains.as_ref(),
977980
)
978981
}
979982

@@ -992,6 +995,15 @@ impl<'a> NetworkFilter<'a> {
992995
return FilterTokens::OptDomains;
993996
}
994997

998+
// A single positive `$to=` is the next most selective key.
999+
if self.opt_to_not_domains.is_none()
1000+
&& let Some(domains) = self.opt_to_domains.as_ref()
1001+
&& let [domain] = domains.as_slice()
1002+
{
1003+
tokens_buffer.push(*domain);
1004+
return FilterTokens::OptToDomains;
1005+
}
1006+
9951007
// Get tokens from filter
9961008
match &self.filter {
9971009
FilterPart::Simple(f) if !self.is_complete_regex() => {
@@ -1046,6 +1058,14 @@ impl<'a> NetworkFilter<'a> {
10461058
}
10471059
// Too many domains to bucket individually; fall back to the catch-all
10481060
// bucket (token 0).
1061+
} else if let Some(opt_to_domains) = self.opt_to_domains.as_ref()
1062+
&& !opt_to_domains.is_empty()
1063+
{
1064+
let cap = tokens_buffer.remaining_capacity();
1065+
if opt_to_domains.len() <= cap {
1066+
tokens_buffer.extend(opt_to_domains.iter().copied());
1067+
return FilterTokens::OptToDomains;
1068+
}
10491069
}
10501070
FilterTokens::Empty
10511071
} else {
@@ -1146,6 +1166,8 @@ fn compute_filter_id(
11461166
hostname: Option<&str>,
11471167
opt_domains: Option<&Vec<Hash>>,
11481168
opt_not_domains: Option<&Vec<Hash>>,
1169+
opt_to_domains: Option<&Vec<Hash>>,
1170+
opt_to_not_domains: Option<&Vec<Hash>>,
11491171
) -> Hash {
11501172
let mut hasher = FxHasher::default();
11511173

@@ -1171,6 +1193,18 @@ fn compute_filter_id(
11711193
}
11721194
}
11731195

1196+
if let Some(domains) = opt_to_domains {
1197+
for d in domains {
1198+
hasher.write_u64(*d);
1199+
}
1200+
}
1201+
1202+
if let Some(domains) = opt_to_not_domains {
1203+
for d in domains {
1204+
hasher.write_u64(*d);
1205+
}
1206+
}
1207+
11741208
match filter {
11751209
FilterPart::Empty => {}
11761210
FilterPart::Simple(s) => write_str_to_hasher(&mut hasher, s.as_ref()),

src/filters/network_matchers.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -421,21 +421,21 @@ pub fn check_options(mask: NetworkFilterMask, request: &request::Request) -> boo
421421
#[inline]
422422
pub fn check_included_domains_mapped(
423423
opt_domains: Option<&[u32]>,
424-
request: &request::Request,
424+
hostname_hashes: Option<&[Hash]>,
425425
mapping: &HashMap<Hash, u32>,
426426
) -> bool {
427-
// Source URL must be among these domains to match
427+
// Hostname must be among these domains to match
428428
if let Some(included_domains) = opt_domains.as_ref() {
429-
if let Some(source_hashes) = request.source_hostname_hashes.as_ref() {
430-
if source_hashes.iter().all(|h| {
429+
if let Some(hashes) = hostname_hashes {
430+
if hashes.iter().all(|h| {
431431
mapping
432432
.get(h)
433433
.is_none_or(|index| !utils::bin_lookup(included_domains, *index))
434434
}) {
435435
return false;
436436
}
437437
} else {
438-
// If there are domain restrictions but no source hostname, we can't apply the rule
438+
// If there are domain restrictions but no hostname, we can't apply the rule
439439
return false;
440440
}
441441
}
@@ -445,21 +445,21 @@ pub fn check_included_domains_mapped(
445445
#[inline]
446446
pub fn check_excluded_domains_mapped(
447447
opt_not_domains: Option<&[u32]>,
448-
request: &request::Request,
448+
hostname_hashes: Option<&[Hash]>,
449449
mapping: &HashMap<Hash, u32>,
450450
) -> bool {
451451
if let Some(excluded_domains) = opt_not_domains.as_ref() {
452-
if let Some(source_hashes) = request.source_hostname_hashes.as_ref() {
453-
if source_hashes.iter().any(|h| {
452+
if let Some(hashes) = hostname_hashes {
453+
if hashes.iter().any(|h| {
454454
mapping
455455
.get(h)
456456
.is_some_and(|index| utils::bin_lookup(excluded_domains, *index))
457457
}) {
458458
return false;
459459
}
460460
} else {
461-
// If there are domain restrictions but no source hostname
462-
// (i.e. about:blank), apply the rule anyway.
461+
// If there are domain restrictions but no hostname
462+
// (i.e. about:blank for source), apply the rule anyway.
463463
return true;
464464
}
465465
}

src/flatbuffers/fb_network_filter.fbs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ table NetworkFilterList {
4747
/// `$domain=` / `$from=` token buckets.
4848
opt_domains_map_index: [uint32] (required);
4949
opt_domains_map_values: [NetworkFilter] (required);
50+
51+
/// `$to=` token buckets.
52+
opt_to_domains_map_index: [uint32] (required);
53+
opt_to_domains_map_values: [NetworkFilter] (required);
5054
}
5155

5256
/// A table to store the most host-specific cosmetic rules.

0 commit comments

Comments
 (0)