diff --git a/common/strmatcher/benchmark_test.go b/common/strmatcher/benchmark_test.go index d92ec70c..2915a410 100644 --- a/common/strmatcher/benchmark_test.go +++ b/common/strmatcher/benchmark_test.go @@ -21,6 +21,19 @@ func BenchmarkDomainMatcherGroup(b *testing.B) { } } +func BenchmarkFullMatcherGroup(b *testing.B) { + g := new(FullMatcherGroup) + + for i := 1; i <= 1024; i++ { + g.Add(strconv.Itoa(i)+".v2ray.com", uint32(i)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = g.Match("0.v2ray.com") + } +} + func BenchmarkMarchGroup(b *testing.B) { g := NewMatcherGroup() for i := 1; i <= 1024; i++ { diff --git a/common/strmatcher/domain_matcher.go b/common/strmatcher/domain_matcher.go index 46a755d3..1d8cb067 100644 --- a/common/strmatcher/domain_matcher.go +++ b/common/strmatcher/domain_matcher.go @@ -37,6 +37,10 @@ func (g *DomainMatcherGroup) Add(domain string, value uint32) { current.value = value } +func (g *DomainMatcherGroup) addMatcher(m domainMatcher, value uint32) { + g.Add(string(m), value) +} + func (g *DomainMatcherGroup) Match(domain string) uint32 { current := g.root if current == nil { diff --git a/common/strmatcher/full_matcher.go b/common/strmatcher/full_matcher.go new file mode 100644 index 00000000..fc7e0c33 --- /dev/null +++ b/common/strmatcher/full_matcher.go @@ -0,0 +1,25 @@ +package strmatcher + +type FullMatcherGroup struct { + matchers map[string]uint32 +} + +func (g *FullMatcherGroup) Add(domain string, value uint32) { + if g.matchers == nil { + g.matchers = make(map[string]uint32) + } + + g.matchers[domain] = value +} + +func (g *FullMatcherGroup) addMatcher(m fullMatcher, value uint32) { + g.Add(string(m), value) +} + +func (g *FullMatcherGroup) Match(str string) uint32 { + if g.matchers == nil { + return 0 + } + + return g.matchers[str] +} diff --git a/common/strmatcher/full_matcher_test.go b/common/strmatcher/full_matcher_test.go new file mode 100644 index 00000000..a19a60c7 --- /dev/null +++ b/common/strmatcher/full_matcher_test.go @@ -0,0 +1,43 @@ +package strmatcher_test + +import ( + "testing" + + . "v2ray.com/core/common/strmatcher" +) + +func TestFullMatcherGroup(t *testing.T) { + g := new(FullMatcherGroup) + g.Add("v2ray.com", 1) + g.Add("google.com", 2) + g.Add("x.a.com", 3) + + testCases := []struct { + Domain string + Result uint32 + }{ + { + Domain: "v2ray.com", + Result: 1, + }, + { + Domain: "y.com", + Result: 0, + }, + } + + for _, testCase := range testCases { + r := g.Match(testCase.Domain) + if r != testCase.Result { + t.Error("Failed to match domain: ", testCase.Domain, ", expect ", testCase.Result, ", but got ", r) + } + } +} + +func TestEmptyFullMatcherGroup(t *testing.T) { + g := new(FullMatcherGroup) + r := g.Match("v2ray.com") + if r != 0 { + t.Error("Expect 0, but ", r) + } +} diff --git a/common/strmatcher/strmatcher.go b/common/strmatcher/strmatcher.go index a65a68d8..5aa50b81 100644 --- a/common/strmatcher/strmatcher.go +++ b/common/strmatcher/strmatcher.go @@ -53,27 +53,24 @@ type matcherEntry struct { type MatcherGroup struct { count uint32 - fullMatchers map[string]uint32 + fullMatcher FullMatcherGroup domainMatcher DomainMatcherGroup otherMatchers []matcherEntry } func NewMatcherGroup() *MatcherGroup { - return &MatcherGroup{ - count: 1, - fullMatchers: make(map[string]uint32), - } + return &MatcherGroup{} } func (g *MatcherGroup) Add(m Matcher) uint32 { - c := g.count g.count++ + c := g.count switch tm := m.(type) { case fullMatcher: - g.fullMatchers[string(tm)] = c + g.fullMatcher.addMatcher(tm, c) case domainMatcher: - g.domainMatcher.Add(string(tm), c) + g.domainMatcher.addMatcher(tm, c) default: g.otherMatchers = append(g.otherMatchers, matcherEntry{ m: m, @@ -85,7 +82,7 @@ func (g *MatcherGroup) Add(m Matcher) uint32 { } func (g *MatcherGroup) Match(pattern string) uint32 { - if c, f := g.fullMatchers[pattern]; f { + if c := g.fullMatcher.Match(pattern); c > 0 { return c }