strmatcher.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package strmatcher
  2. import (
  3. "regexp"
  4. "sync"
  5. "time"
  6. "v2ray.com/core/common"
  7. "v2ray.com/core/common/task"
  8. )
  9. // Matcher is the interface to determine a string matches a pattern.
  10. type Matcher interface {
  11. // Match returns true if the given string matches a predefined pattern.
  12. Match(string) bool
  13. }
  14. // Type is the type of the matcher.
  15. type Type byte
  16. const (
  17. // Full is the type of matcher that the input string must exactly equal to the pattern.
  18. Full Type = iota
  19. // Substr is the type of matcher that the input string must contain the pattern as a sub-string.
  20. Substr
  21. // Domain is the type of matcher that the input string must be a sub-domain or itself of the pattern.
  22. Domain
  23. // Regex is the type of matcher that the input string must matches the regular-expression pattern.
  24. Regex
  25. )
  26. // New creates a new Matcher based on the given pattern.
  27. func (t Type) New(pattern string) (Matcher, error) {
  28. switch t {
  29. case Full:
  30. return fullMatcher(pattern), nil
  31. case Substr:
  32. return substrMatcher(pattern), nil
  33. case Domain:
  34. return domainMatcher(pattern), nil
  35. case Regex:
  36. r, err := regexp.Compile(pattern)
  37. if err != nil {
  38. return nil, err
  39. }
  40. return &regexMatcher{
  41. pattern: r,
  42. }, nil
  43. default:
  44. panic("Unknown type")
  45. }
  46. }
  47. // IndexMatcher is the interface for matching with a group of matchers.
  48. type IndexMatcher interface {
  49. // Match returns the the index of a matcher that matches the input. It returns 0 if no such matcher exists.
  50. Match(input string) uint32
  51. }
  52. type matcherEntry struct {
  53. m Matcher
  54. id uint32
  55. }
  56. // MatcherGroup is an implementation of IndexMatcher.
  57. // Empty initialization works.
  58. type MatcherGroup struct {
  59. count uint32
  60. fullMatcher FullMatcherGroup
  61. domainMatcher DomainMatcherGroup
  62. otherMatchers []matcherEntry
  63. }
  64. // Add adds a new Matcher into the MatcherGroup, and returns its index. The index will never be 0.
  65. func (g *MatcherGroup) Add(m Matcher) uint32 {
  66. g.count++
  67. c := g.count
  68. switch tm := m.(type) {
  69. case fullMatcher:
  70. g.fullMatcher.addMatcher(tm, c)
  71. case domainMatcher:
  72. g.domainMatcher.addMatcher(tm, c)
  73. default:
  74. g.otherMatchers = append(g.otherMatchers, matcherEntry{
  75. m: m,
  76. id: c,
  77. })
  78. }
  79. return c
  80. }
  81. // Match implements IndexMatcher.Match.
  82. func (g *MatcherGroup) Match(pattern string) uint32 {
  83. if c := g.fullMatcher.Match(pattern); c > 0 {
  84. return c
  85. }
  86. if c := g.domainMatcher.Match(pattern); c > 0 {
  87. return c
  88. }
  89. for _, e := range g.otherMatchers {
  90. if e.m.Match(pattern) {
  91. return e.id
  92. }
  93. }
  94. return 0
  95. }
  96. // Size returns the number of matchers in the MatcherGroup.
  97. func (g *MatcherGroup) Size() uint32 {
  98. return g.count
  99. }
  100. type cacheEntry struct {
  101. timestamp time.Time
  102. result uint32
  103. }
  104. // CachedMatcherGroup is a IndexMatcher with cachable results.
  105. type CachedMatcherGroup struct {
  106. sync.RWMutex
  107. group *MatcherGroup
  108. cache map[string]cacheEntry
  109. cleanup *task.Periodic
  110. }
  111. // NewCachedMatcherGroup creats a new CachedMatcherGroup.
  112. func NewCachedMatcherGroup(g *MatcherGroup) *CachedMatcherGroup {
  113. r := &CachedMatcherGroup{
  114. group: g,
  115. cache: make(map[string]cacheEntry),
  116. }
  117. r.cleanup = &task.Periodic{
  118. Interval: time.Second * 30,
  119. Execute: func() error {
  120. r.Lock()
  121. defer r.Unlock()
  122. if len(r.cache) == 0 {
  123. return nil
  124. }
  125. expire := time.Now().Add(-1 * time.Second * 120)
  126. for p, e := range r.cache {
  127. if e.timestamp.Before(expire) {
  128. delete(r.cache, p)
  129. }
  130. }
  131. if len(r.cache) == 0 {
  132. r.cache = make(map[string]cacheEntry)
  133. }
  134. return nil
  135. },
  136. }
  137. common.Must(r.cleanup.Start())
  138. return r
  139. }
  140. // Match implements IndexMatcher.Match.
  141. func (g *CachedMatcherGroup) Match(pattern string) uint32 {
  142. g.RLock()
  143. r, f := g.cache[pattern]
  144. g.RUnlock()
  145. if f {
  146. return r.result
  147. }
  148. mr := g.group.Match(pattern)
  149. g.Lock()
  150. g.cache[pattern] = cacheEntry{
  151. result: mr,
  152. timestamp: time.Now(),
  153. }
  154. g.Unlock()
  155. return mr
  156. }