Add dump for domain matcher

This commit is contained in:
世界 2024-07-02 15:49:37 +08:00
parent 0b4c0a1283
commit c37f988a4f
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
3 changed files with 115 additions and 0 deletions

View file

@ -72,6 +72,35 @@ func (m *Matcher) Write(writer varbin.Writer) error {
})
}
func (m *Matcher) Dump() (domainList []string, prefixList []string) {
domainMap := make(map[string]bool)
prefixMap := make(map[string]bool)
for _, key := range m.set.keys() {
key = reverseDomain(key)
if key[0] == prefixLabel {
prefixMap[key[1:]] = true
} else {
domainMap[key] = true
}
}
for rawPrefix := range prefixMap {
if rawPrefix[0] == '.' {
if rootDomain := rawPrefix[1:]; domainMap[rootDomain] {
delete(domainMap, rootDomain)
prefixList = append(prefixList, rootDomain)
continue
}
}
prefixList = append(prefixList, rawPrefix)
}
for domain := range domainMap {
domainList = append(domainList, domain)
}
sort.Strings(domainList)
sort.Strings(prefixList)
return domainList, prefixList
}
func reverseDomain(domain string) string {
l := len(domain)
b := make([]byte, l)

View file

@ -0,0 +1,58 @@
package domain_test
import (
"encoding/json"
"net/http"
"sort"
"testing"
"github.com/sagernet/sing/common/domain"
"github.com/stretchr/testify/require"
)
func TestMatcher(t *testing.T) {
testDomain := []string{"example.com", "example.org"}
testDomainSuffix := []string{".com.cn", ".org.cn", "sagernet.org"}
matcher := domain.NewMatcher(testDomain, testDomainSuffix)
require.NotNil(t, matcher)
require.True(t, matcher.Match("example.com"))
require.True(t, matcher.Match("example.org"))
require.False(t, matcher.Match("example.cn"))
require.True(t, matcher.Match("example.com.cn"))
require.True(t, matcher.Match("example.org.cn"))
require.False(t, matcher.Match("com.cn"))
require.False(t, matcher.Match("org.cn"))
require.True(t, matcher.Match("sagernet.org"))
require.True(t, matcher.Match("sing-box.sagernet.org"))
dDomain, dDomainSuffix := matcher.Dump()
require.Equal(t, testDomain, dDomain)
require.Equal(t, testDomainSuffix, dDomainSuffix)
}
type simpleRuleSet struct {
Rules []struct {
Domain []string `json:"domain"`
DomainSuffix []string `json:"domain_suffix"`
}
}
func TestDumpLarge(t *testing.T) {
response, err := http.Get("https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/sing/geo/geosite/cn.json")
require.NoError(t, err)
defer response.Body.Close()
var ruleSet simpleRuleSet
err = json.NewDecoder(response.Body).Decode(&ruleSet)
require.NoError(t, err)
domainList := ruleSet.Rules[0].Domain
domainSuffixList := ruleSet.Rules[0].DomainSuffix
require.Len(t, ruleSet.Rules, 1)
require.True(t, len(domainList)+len(domainSuffixList) > 0)
sort.Strings(domainList)
sort.Strings(domainSuffixList)
matcher := domain.NewMatcher(domainList, domainSuffixList)
require.NotNil(t, matcher)
dDomain, dDomainSuffix := matcher.Dump()
require.Equal(t, domainList, dDomain)
require.Equal(t, domainSuffixList, dDomainSuffix)
}

View file

@ -74,6 +74,34 @@ func (ss *succinctSet) Has(key string) bool {
}
}
func (ss *succinctSet) keys() []string {
var result []string
var currentKey []byte
var bmIdx, nodeId int
var traverse func(int, int)
traverse = func(nodeId, bmIdx int) {
if getBit(ss.leaves, nodeId) != 0 {
result = append(result, string(currentKey))
}
for ; ; bmIdx++ {
if getBit(ss.labelBitmap, bmIdx) != 0 {
return
}
nextLabel := ss.labels[bmIdx-nodeId]
currentKey = append(currentKey, nextLabel)
nextNodeId := countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)
nextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1
traverse(nextNodeId, nextBmIdx)
currentKey = currentKey[:len(currentKey)-1]
}
}
traverse(nodeId, bmIdx)
return result
}
func setBit(bm *[]uint64, i int, v int) {
for i>>6 >= len(*bm) {
*bm = append(*bm, 0)