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  }