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  }