github.com/database64128/shadowsocks-go@v1.7.0/domainset/matcher_domain.go (about) 1 package domainset 2 3 import ( 4 "github.com/database64128/shadowsocks-go/maps" 5 "github.com/database64128/shadowsocks-go/slices" 6 ) 7 8 // MaxLinearDomains is the maximum number of domain rules under which a linear matcher can outperform a map matcher. 9 const MaxLinearDomains = 16 10 11 // DomainLinearMatcher matches domain rules using linear search. 12 // It is faster than [DomainMapMatcher] when the number of rules is 13 // no greater than [MaxLinearDomains]. 14 type DomainLinearMatcher []string 15 16 // NewDomainLinearMatcher creates a [DomainLinearMatcher] with the specified initial capacity. 17 func NewDomainLinearMatcher(capacity int) MatcherBuilder { 18 dlm := make(DomainLinearMatcher, 0, capacity) 19 return &dlm 20 } 21 22 // Match implements the Matcher Match method. 23 func (dlm DomainLinearMatcher) Match(domain string) bool { 24 return slices.Contains(dlm, domain) 25 } 26 27 // Insert implements the MatcherBuilder Insert method. 28 func (dlmp *DomainLinearMatcher) Insert(rule string) { 29 *dlmp = append(*dlmp, rule) 30 } 31 32 // Rules implements the MatcherBuilder Rules method. 33 func (dlm DomainLinearMatcher) Rules() []string { 34 return dlm 35 } 36 37 // MatcherCount implements the MatcherBuilder MatcherCount method. 38 func (dlm DomainLinearMatcher) MatcherCount() int { 39 if len(dlm) == 0 { 40 return 0 41 } 42 return 1 43 } 44 45 // AppendTo implements the MatcherBuilder AppendTo method. 46 func (dlmp *DomainLinearMatcher) AppendTo(matchers []Matcher) ([]Matcher, error) { 47 dlm := *dlmp 48 49 if len(dlm) == 0 { 50 return matchers, nil 51 } 52 53 if len(dlm) > MaxLinearDomains { 54 dmm := DomainMapMatcherFromSlice(dlm) 55 return dmm.AppendTo(matchers) 56 } 57 58 return append(matchers, dlmp), nil 59 } 60 61 // DomainBinarySearchMatcher matches domain rules using binary search. 62 type DomainBinarySearchMatcher []string 63 64 // NewDomainBinarySearchMatcher creates a [DomainBinarySearchMatcher] with the specified initial capacity. 65 func NewDomainBinarySearchMatcher(capacity int) MatcherBuilder { 66 dbsm := make(DomainBinarySearchMatcher, 0, capacity) 67 return &dbsm 68 } 69 70 // DomainBinarySearchMatcherFromSlice creates a [DomainBinarySearchMatcher] from a slice of domain rules. 71 func DomainBinarySearchMatcherFromSlice(domains []string) DomainBinarySearchMatcher { 72 slices.Sort(domains) 73 return domains 74 } 75 76 // Match implements the Matcher Match method. 77 func (dbsm DomainBinarySearchMatcher) Match(domain string) bool { 78 _, found := slices.BinarySearch(dbsm, domain) 79 return found 80 } 81 82 // Insert implements the MatcherBuilder Insert method. 83 func (dbsmp *DomainBinarySearchMatcher) Insert(rule string) { 84 index, found := slices.BinarySearch(*dbsmp, rule) 85 if !found { 86 *dbsmp = slices.Insert(*dbsmp, index, rule) 87 } 88 } 89 90 // Rules implements the MatcherBuilder Rules method. 91 func (dbsm DomainBinarySearchMatcher) Rules() []string { 92 return dbsm 93 } 94 95 // MatcherCount implements the MatcherBuilder MatcherCount method. 96 func (dbsm DomainBinarySearchMatcher) MatcherCount() int { 97 if len(dbsm) == 0 { 98 return 0 99 } 100 return 1 101 } 102 103 // AppendTo implements the MatcherBuilder AppendTo method. 104 func (dbsmp *DomainBinarySearchMatcher) AppendTo(matchers []Matcher) ([]Matcher, error) { 105 dbsm := *dbsmp 106 107 if len(dbsm) == 0 { 108 return matchers, nil 109 } 110 111 return append(matchers, dbsmp), nil 112 } 113 114 // DomainMapMatcher matches domain rules using a map. 115 // It is faster than [DomainLinearMatcher] when the number of rules is 116 // greater than [MaxLinearDomains]. 117 type DomainMapMatcher map[string]struct{} 118 119 // NewDomainMapMatcher creates a [DomainMapMatcher] with the specified initial capacity. 120 func NewDomainMapMatcher(capacity int) MatcherBuilder { 121 dmm := make(DomainMapMatcher, capacity) 122 return &dmm 123 } 124 125 // DomainMapMatcherFromSlice creates a [DomainMapMatcher] from a slice of domain rules. 126 func DomainMapMatcherFromSlice(domains []string) DomainMapMatcher { 127 dmm := make(DomainMapMatcher, len(domains)) 128 for _, domain := range domains { 129 dmm.Insert(domain) 130 } 131 return dmm 132 } 133 134 // Match implements the Matcher Match method. 135 func (dmm DomainMapMatcher) Match(domain string) bool { 136 _, ok := dmm[domain] 137 return ok 138 } 139 140 // Insert implements the MatcherBuilder Insert method. 141 func (dmm DomainMapMatcher) Insert(rule string) { 142 dmm[rule] = struct{}{} 143 } 144 145 // Rules implements the MatcherBuilder Rules method. 146 func (dmm DomainMapMatcher) Rules() []string { 147 return maps.Keys(dmm) 148 } 149 150 // MatcherCount implements the MatcherBuilder MatcherCount method. 151 func (dmm DomainMapMatcher) MatcherCount() int { 152 if len(dmm) == 0 { 153 return 0 154 } 155 return 1 156 } 157 158 // AppendTo implements the MatcherBuilder AppendTo method. 159 func (dmmp *DomainMapMatcher) AppendTo(matchers []Matcher) ([]Matcher, error) { 160 dmm := *dmmp 161 162 if len(dmm) == 0 { 163 return matchers, nil 164 } 165 166 if len(dmm) <= MaxLinearDomains { 167 dlm := DomainLinearMatcher(maps.Keys(dmm)) 168 return dlm.AppendTo(matchers) 169 } 170 171 return append(matchers, dmm), nil 172 }