From 5dd30db805ebee8797663deee5e8cad4011df614 Mon Sep 17 00:00:00 2001 From: MkQtS <81752398+MkQtS@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:35:11 +0800 Subject: [PATCH] main.go: support to trim subdomains with attrs (#3912) --- main.go | 34 ++++++++++++++++++++-------------- main_test.go | 12 ++++++------ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index 2f5fad39..c3753bb3 100644 --- a/main.go +++ b/main.go @@ -408,33 +408,39 @@ func isMatchAttrFilters(entry *Entry, incFilter *Inclusion) bool { return true } +// polishList trims redundant full/domain type subdomains and returns sorted lists +// A domain with attr(s) trims subdomains with same attr(s) and subdomains without attr +// A subdomain with attr(s) can only be trimed by parent domain with same attr(s) func polishList(roughMap map[string]*Entry) []*Entry { finalList := make([]*Entry, 0, len(roughMap)) - queuingList := make([]*Entry, 0, len(roughMap)) // Domain/full entries without attr - domainsMap := make(map[string]bool) + queuingList := make([]*Entry, 0, len(roughMap)) + parentsMap := make(map[string]bool) for _, entry := range roughMap { - switch entry.Type { // Bypass regexp, keyword and "full/domain with attr" + switch entry.Type { // Bypass regexp and keyword case dlc.RuleTypeRegexp, dlc.RuleTypeKeyword: finalList = append(finalList, entry) case dlc.RuleTypeDomain: - domainsMap[entry.Value] = true + parentsMap[entry.Value] = true if len(entry.Attrs) != 0 { - finalList = append(finalList, entry) - } else { - queuingList = append(queuingList, entry) + // `sub.example.org:@attr1,@attr2` + // Ensure no dot exists except the domain (entry.Value) part + _, domainAndAttrs, _ := strings.Cut(entry.Plain, ":") + parentsMap[domainAndAttrs] = true } + queuingList = append(queuingList, entry) case dlc.RuleTypeFullDomain: - if len(entry.Attrs) != 0 { - finalList = append(finalList, entry) - } else { - queuingList = append(queuingList, entry) - } + queuingList = append(queuingList, entry) } } // Remove redundant subdomains for full/domain without attr for _, qentry := range queuingList { isRedundant := false - pd := qentry.Value // To be parent domain + var pd string // To be parent domain (with attrs) + if len(qentry.Attrs) == 0 { + pd = qentry.Value + } else { + _, pd, _ = strings.Cut(qentry.Plain, ":") + } if qentry.Type == dlc.RuleTypeFullDomain { pd = "." + pd // So that `domain:example.org` overrides `full:example.org` } @@ -444,7 +450,7 @@ func polishList(roughMap map[string]*Entry) []*Entry { if !hasParent { break } - if domainsMap[pd] { + if parentsMap[pd] { isRedundant = true break } diff --git a/main_test.go b/main_test.go index c0b7f017..a7eb5cfa 100644 --- a/main_test.go +++ b/main_test.go @@ -108,12 +108,12 @@ func TestParseInclusion(t *testing.T) { func TestPolishList(t *testing.T) { rules := []struct{ typ, rule string }{ - {"domain", "example.com"}, - {"domain", "sub.example.com"}, // Redundant - {"full", "www.example.com"}, // Redundant - {"full", "example.com"}, // Redundant + {"domain", "example.com @cn"}, + {"domain", "sub.example.com"}, // Redundant, no attribute + {"full", "www.example.com @cn"}, // Redundant, same attribute + {"full", "example.com"}, // Redundant, no attribute {"full", "example.org"}, // Kept, no parent domain rule - {"domain", "ads.example.com @ads"}, // Kept, has attribute + {"domain", "ads.example.com @ads"}, // Kept, different attribute {"keyword", "example"}, } roughMap := make(map[string]*Entry, len(rules)) @@ -124,7 +124,7 @@ func TestPolishList(t *testing.T) { } roughMap[entry.Plain] = entry } - want := []string{"domain:ads.example.com:@ads", "domain:example.com", "full:example.org", "keyword:example"} + want := []string{"domain:ads.example.com:@ads", "domain:example.com:@cn", "full:example.org", "keyword:example"} assertPlains(t, "polishList", polishList(roughMap), want) }