strmatcher.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. expire := time.Now().Add(-1 * time.Second * 120)
  123. for p, e := range r.cache {
  124. if e.timestamp.Before(expire) {
  125. delete(r.cache, p)
  126. }
  127. }
  128. return nil
  129. },
  130. }
  131. common.Must(r.cleanup.Start())
  132. return r
  133. }
  134. // Match implements IndexMatcher.Match.
  135. func (g *CachedMatcherGroup) Match(pattern string) uint32 {
  136. g.RLock()
  137. r, f := g.cache[pattern]
  138. g.RUnlock()
  139. if f {
  140. return r.result
  141. }
  142. mr := g.group.Match(pattern)
  143. g.Lock()
  144. g.cache[pattern] = cacheEntry{
  145. result: mr,
  146. timestamp: time.Now(),
  147. }
  148. g.Unlock()
  149. return mr
  150. }