matchergroup_domain.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package strmatcher
  2. import "strings"
  3. func breakDomain(domain string) []string {
  4. return strings.Split(domain, ".")
  5. }
  6. type node struct {
  7. values []uint32
  8. sub map[string]*node
  9. }
  10. // DomainMatcherGroup is an implementation of MatcherGroup.
  11. // It uses trie to optimize both memory consumption and lookup speed. Trie node is domain label based.
  12. type DomainMatcherGroup struct {
  13. root *node
  14. }
  15. // AddDomainMatcher implements MatcherGroupForDomain.AddDomainMatcher.
  16. func (g *DomainMatcherGroup) AddDomainMatcher(matcher DomainMatcher, value uint32) {
  17. if g.root == nil {
  18. g.root = new(node)
  19. }
  20. current := g.root
  21. parts := breakDomain(matcher.Pattern())
  22. for i := len(parts) - 1; i >= 0; i-- {
  23. part := parts[i]
  24. if current.sub == nil {
  25. current.sub = make(map[string]*node)
  26. }
  27. next := current.sub[part]
  28. if next == nil {
  29. next = new(node)
  30. current.sub[part] = next
  31. }
  32. current = next
  33. }
  34. current.values = append(current.values, value)
  35. }
  36. // Match implements MatcherGroup.Match.
  37. func (g *DomainMatcherGroup) Match(domain string) []uint32 {
  38. if domain == "" {
  39. return nil
  40. }
  41. current := g.root
  42. if current == nil {
  43. return nil
  44. }
  45. nextPart := func(idx int) int {
  46. for i := idx - 1; i >= 0; i-- {
  47. if domain[i] == '.' {
  48. return i
  49. }
  50. }
  51. return -1
  52. }
  53. matches := [][]uint32{}
  54. idx := len(domain)
  55. for {
  56. if idx == -1 || current.sub == nil {
  57. break
  58. }
  59. nidx := nextPart(idx)
  60. part := domain[nidx+1 : idx]
  61. next := current.sub[part]
  62. if next == nil {
  63. break
  64. }
  65. current = next
  66. idx = nidx
  67. if len(current.values) > 0 {
  68. matches = append(matches, current.values)
  69. }
  70. }
  71. switch len(matches) {
  72. case 0:
  73. return nil
  74. case 1:
  75. return matches[0]
  76. default:
  77. result := []uint32{}
  78. for idx := range matches {
  79. // Insert reversely, the subdomain that matches further ranks higher
  80. result = append(result, matches[len(matches)-1-idx]...)
  81. }
  82. return result
  83. }
  84. }
  85. // MatchAny implements MatcherGroup.MatchAny.
  86. func (g *DomainMatcherGroup) MatchAny(domain string) bool {
  87. return len(g.Match(domain)) > 0
  88. }