mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-02-27 09:17:12 +00:00
feat: Add succinct matcher support for GeoSite
and use it by default
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
mph:minimal 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user