github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/domainset/matcher_suffix.go (about) 1 package domainset 2 3 import "github.com/database64128/shadowsocks-go/maphelper" 4 5 // MaxLinearSuffixes is the maximum number of suffix rules under which a linear matcher can outperform a trie matcher. 6 const MaxLinearSuffixes = 4 7 8 // SuffixLinearMatcher matches suffix rules by iterating over the suffixes. 9 // It is faster than [SuffixTrieMatcher] when the number of rules is 10 // no greater than [MaxLinearSuffixes]. 11 type SuffixLinearMatcher []string 12 13 // NewSuffixLinearMatcher creates a [SuffixLinearMatcher] with the specified initial capacity. 14 func NewSuffixLinearMatcher(capacity int) MatcherBuilder { 15 slm := make(SuffixLinearMatcher, 0, capacity) 16 return &slm 17 } 18 19 // Match implements the Matcher Match method. 20 func (slm SuffixLinearMatcher) Match(domain string) bool { 21 for _, suffix := range slm { 22 if matchDomainSuffix(domain, suffix) { 23 return true 24 } 25 } 26 return false 27 } 28 29 // Insert implements the MatcherBuilder Insert method. 30 func (slmp *SuffixLinearMatcher) Insert(rule string) { 31 *slmp = append(*slmp, rule) 32 } 33 34 // Rules implements the MatcherBuilder Rules method. 35 func (slm SuffixLinearMatcher) Rules() []string { 36 return slm 37 } 38 39 // MatcherCount implements the MatcherBuilder MatcherCount method. 40 func (slm SuffixLinearMatcher) MatcherCount() int { 41 if len(slm) == 0 { 42 return 0 43 } 44 return 1 45 } 46 47 // AppendTo implements the MatcherBuilder AppendTo method. 48 func (slmp *SuffixLinearMatcher) AppendTo(matchers []Matcher) ([]Matcher, error) { 49 slm := *slmp 50 51 if len(slm) == 0 { 52 return matchers, nil 53 } 54 55 if len(slm) > MaxLinearSuffixes { 56 return append(matchers, DomainSuffixTrieFromSlice(slm)), nil 57 } 58 59 return append(matchers, slmp), nil 60 } 61 62 func matchDomainSuffix(domain, suffix string) bool { 63 return domain == suffix || len(domain) > len(suffix) && domain[len(domain)-len(suffix)-1] == '.' && domain[len(domain)-len(suffix):] == suffix 64 } 65 66 // SuffixMapMatcher matches suffix rules using a single map. 67 type SuffixMapMatcher map[string]struct{} 68 69 // NewSuffixMapMatcher creates a [SuffixMapMatcher] with the specified initial capacity. 70 func NewSuffixMapMatcher(capacity int) MatcherBuilder { 71 smm := make(SuffixMapMatcher, capacity) 72 return &smm 73 } 74 75 // SuffixMapMatcherFromSlice creates a [SuffixMapMatcher] from a slice of suffix rules. 76 func SuffixMapMatcherFromSlice(suffixes []string) SuffixMapMatcher { 77 smm := make(SuffixMapMatcher, len(suffixes)) 78 for _, suffix := range suffixes { 79 smm.Insert(suffix) 80 } 81 return smm 82 } 83 84 // Match implements the Matcher Match method. 85 func (smm SuffixMapMatcher) Match(domain string) bool { 86 for i := len(domain) - 1; i >= 0; i-- { 87 if domain[i] != '.' { 88 continue 89 } 90 if _, ok := smm[domain[i+1:]]; ok { 91 return true 92 } 93 } 94 _, ok := smm[domain] 95 return ok 96 } 97 98 // Insert implements the MatcherBuilder Insert method. 99 func (smm SuffixMapMatcher) Insert(rule string) { 100 smm[rule] = struct{}{} 101 } 102 103 // Rules implements the MatcherBuilder Rules method. 104 func (smm SuffixMapMatcher) Rules() []string { 105 return maphelper.Keys(smm) 106 } 107 108 // MatcherCount implements the MatcherBuilder MatcherCount method. 109 func (smm SuffixMapMatcher) MatcherCount() int { 110 if len(smm) == 0 { 111 return 0 112 } 113 return 1 114 } 115 116 // AppendTo implements the MatcherBuilder AppendTo method. 117 func (smmp *SuffixMapMatcher) AppendTo(matchers []Matcher) ([]Matcher, error) { 118 smm := *smmp 119 120 if len(smm) == 0 { 121 return matchers, nil 122 } 123 124 if len(smm) <= MaxLinearSuffixes { 125 slm := SuffixLinearMatcher(maphelper.Keys(smm)) 126 return slm.AppendTo(matchers) 127 } 128 129 return append(matchers, smmp), nil 130 }