feat: Add succinct matcher support for GeoSite

and use it by default
This commit is contained in:
H1JK
2023-12-17 00:00:35 +08:00
parent 5b23b979df
commit 2bba8aa14a
9 changed files with 122 additions and 35 deletions

View File

@@ -8,6 +8,7 @@ import (
"strings"
"github.com/metacubex/mihomo/component/geodata/strmatcher"
"github.com/metacubex/mihomo/component/trie"
)
var matcherTypeMap = map[Domain_Type]strmatcher.Type{
@@ -31,12 +32,69 @@ func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
return matcher, nil
}
type DomainMatcher struct {
type DomainMatcher interface {
ApplyDomain(string) bool
}
type succinctDomainMatcher struct {
set *trie.DomainSet
otherMatchers []strmatcher.Matcher
not bool
}
func (m succinctDomainMatcher) ApplyDomain(domain string) bool {
isMatched := m.set.Has(domain)
if !isMatched {
for _, matcher := range m.otherMatchers {
isMatched = matcher.Match(domain)
if isMatched {
break
}
}
}
if m.not {
isMatched = !isMatched
}
return isMatched
}
func NewSuccinctMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
t := trie.New[struct{}]()
m := &succinctDomainMatcher{
not: not,
}
for _, d := range domains {
switch d.Type {
case Domain_Plain, Domain_Regex:
matcher, err := matcherTypeMap[d.Type].New(d.Value)
if err != nil {
return nil, err
}
m.otherMatchers = append(m.otherMatchers, matcher)
case Domain_Domain:
err := t.Insert("+."+d.Value, struct{}{})
if err != nil {
return nil, err
}
case Domain_Full:
err := t.Insert(d.Value, struct{}{})
if err != nil {
return nil, err
}
}
}
m.set = t.NewDomainSet()
return m, nil
}
type v2rayDomainMatcher struct {
matchers strmatcher.IndexMatcher
not bool
}
func NewMphMatcherGroup(domains []*Domain, not bool) (*DomainMatcher, error) {
func NewMphMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
g := strmatcher.NewMphMatcherGroup()
for _, d := range domains {
matcherType, f := matcherTypeMap[d.Type]
@@ -49,14 +107,13 @@ func NewMphMatcherGroup(domains []*Domain, not bool) (*DomainMatcher, error) {
}
}
g.Build()
return &DomainMatcher{
return &v2rayDomainMatcher{
matchers: g,
not: not,
}, nil
}
// NewDomainMatcher new domain matcher.
func NewDomainMatcher(domains []*Domain, not bool) (*DomainMatcher, error) {
func NewDomainMatcher(domains []*Domain, not bool) (DomainMatcher, error) {
g := new(strmatcher.MatcherGroup)
for _, d := range domains {
m, err := domainToMatcher(d)
@@ -66,13 +123,13 @@ func NewDomainMatcher(domains []*Domain, not bool) (*DomainMatcher, error) {
g.Add(m)
}
return &DomainMatcher{
return &v2rayDomainMatcher{
matchers: g,
not: not,
}, nil
}
func (m *DomainMatcher) ApplyDomain(domain string) bool {
func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {
isMatched := len(m.matchers.Match(strings.ToLower(domain))) > 0
if m.not {
isMatched = !isMatched

View File

@@ -12,6 +12,7 @@ import (
)
var geoLoaderName = "memconservative"
var geoSiteMatcher = "succinct"
// geoLoaderName = "standard"
@@ -19,6 +20,10 @@ func LoaderName() string {
return geoLoaderName
}
func SiteMatcherName() string {
return geoSiteMatcher
}
func SetLoader(newLoader string) {
if newLoader == "memc" {
newLoader = "memconservative"
@@ -26,6 +31,16 @@ func SetLoader(newLoader string) {
geoLoaderName = newLoader
}
func SetSiteMatcher(newMatcher string) {
switch newMatcher {
case "hybrid":
newMatcher = "mph"
default:
newMatcher = "succinct"
}
geoSiteMatcher = newMatcher
}
func Verify(name string) error {
switch name {
case C.GeositeName:
@@ -41,8 +56,8 @@ func Verify(name string) error {
var loadGeoSiteMatcherSF = singleflight.Group{}
func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) {
if len(countryCode) == 0 {
func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, int, error) {
if countryCode == "" {
return nil, 0, fmt.Errorf("country code could not be empty")
}
@@ -60,7 +75,7 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error)
listName := strings.TrimSpace(parts[0])
attrVal := parts[1:]
if len(listName) == 0 {
if listName == "" {
return nil, 0, fmt.Errorf("empty listname in rule: %s", countryCode)
}
@@ -104,7 +119,12 @@ func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error)
matcher, err := router.NewDomainMatcher(domains)
mphminimal perfect hash algorithm
*/
matcher, err := router.NewMphMatcherGroup(domains, not)
var matcher router.DomainMatcher
if geoSiteMatcher == "mph" {
matcher, err = router.NewMphMatcherGroup(domains, not)
} else {
matcher, err = router.NewSuccinctMatcherGroup(domains, not)
}
if err != nil {
return nil, 0, err
}