github.com/sagernet/sing@v0.2.6/common/domain/matcher.go (about)

     1  package domain
     2  
     3  import (
     4  	"sort"
     5  	"unicode/utf8"
     6  )
     7  
     8  type Matcher struct {
     9  	set *succinctSet
    10  }
    11  
    12  func NewMatcher(domains []string, domainSuffix []string) *Matcher {
    13  	domainList := make([]string, 0, len(domains)+len(domainSuffix))
    14  	seen := make(map[string]bool, len(domainList))
    15  	for _, domain := range domainSuffix {
    16  		if seen[domain] {
    17  			continue
    18  		}
    19  		seen[domain] = true
    20  		domainList = append(domainList, reverseDomainSuffix(domain))
    21  	}
    22  	for _, domain := range domains {
    23  		if seen[domain] {
    24  			continue
    25  		}
    26  		seen[domain] = true
    27  		domainList = append(domainList, reverseDomain(domain))
    28  	}
    29  	sort.Strings(domainList)
    30  	return &Matcher{
    31  		newSuccinctSet(domainList),
    32  	}
    33  }
    34  
    35  func (m *Matcher) Match(domain string) bool {
    36  	return m.set.Has(reverseDomain(domain))
    37  }
    38  
    39  func reverseDomain(domain string) string {
    40  	l := len(domain)
    41  	b := make([]byte, l)
    42  	for i := 0; i < l; {
    43  		r, n := utf8.DecodeRuneInString(domain[i:])
    44  		i += n
    45  		utf8.EncodeRune(b[l-i:], r)
    46  	}
    47  	return string(b)
    48  }
    49  
    50  func reverseDomainSuffix(domain string) string {
    51  	l := len(domain)
    52  	b := make([]byte, l+1)
    53  	for i := 0; i < l; {
    54  		r, n := utf8.DecodeRuneInString(domain[i:])
    55  		i += n
    56  		utf8.EncodeRune(b[l-i:], r)
    57  	}
    58  	b[l] = prefixLabel
    59  	return string(b)
    60  }