github.com/influxdata/telegraf@v1.30.3/internal/templating/node.go (about) 1 package templating 2 3 import ( 4 "sort" 5 "strings" 6 ) 7 8 // node is an item in a sorted k-ary tree of filter parts. Each child is sorted by its part value. 9 // The special value of "*", is always sorted last. 10 type node struct { 11 separator string 12 value string 13 children nodes 14 template *Template 15 } 16 17 // insert inserts the given string template into the tree. The filter string is separated 18 // on the template separator and each part is used as the path in the tree. 19 func (n *node) insert(filter string, template *Template) { 20 n.separator = template.separator 21 n.recursiveInsert(strings.Split(filter, n.separator), template) 22 } 23 24 // recursiveInsert does the actual recursive insertion 25 func (n *node) recursiveInsert(values []string, template *Template) { 26 // Add the end, set the template 27 if len(values) == 0 { 28 n.template = template 29 return 30 } 31 32 // See if the current element already exists in the tree. If so, insert the 33 // into that sub-tree 34 for _, v := range n.children { 35 if v.value == values[0] { 36 v.recursiveInsert(values[1:], template) 37 return 38 } 39 } 40 41 // New element, add it to the tree and sort the children 42 newNode := &node{value: values[0]} 43 n.children = append(n.children, newNode) 44 sort.Sort(&n.children) 45 46 // Now insert the rest of the tree into the new element 47 newNode.recursiveInsert(values[1:], template) 48 } 49 50 // search searches for a template matching the input string 51 func (n *node) search(line string) *Template { 52 separator := n.separator 53 return n.recursiveSearch(strings.Split(line, separator)) 54 } 55 56 // recursiveSearch performs the actual recursive search 57 func (n *node) recursiveSearch(lineParts []string) *Template { 58 // nothing to search 59 if len(lineParts) == 0 || len(n.children) == 0 { 60 return n.template 61 } 62 63 var ( 64 hasWildcard bool 65 length = len(n.children) 66 ) 67 68 // exclude last child from search if it is a wildcard. sort.Search expects 69 // a lexicographically sorted set of children and we have artificially sorted 70 // wildcards to the end of the child set 71 // wildcards will be searched separately if no exact match is found 72 if hasWildcard = n.children[length-1].value == "*"; hasWildcard { 73 length-- 74 } 75 76 i := sort.Search(length, func(i int) bool { 77 return n.children[i].value >= lineParts[0] 78 }) 79 80 // given an exact match is found within children set 81 if i < length && n.children[i].value == lineParts[0] { 82 // descend into the matching node 83 if tmpl := n.children[i].recursiveSearch(lineParts[1:]); tmpl != nil { 84 // given a template is found return it 85 return tmpl 86 } 87 } 88 89 // given no template is found and the last child is a wildcard 90 if hasWildcard { 91 // also search the wildcard child node 92 return n.children[length].recursiveSearch(lineParts[1:]) 93 } 94 95 // fallback to returning template at this node 96 return n.template 97 } 98 99 // nodes is simply an array of nodes implementing the sorting interface. 100 type nodes []*node 101 102 // Less returns a boolean indicating whether the filter at position j 103 // is less than the filter at position k. Filters are order by string 104 // comparison of each component parts. A wildcard value "*" is never 105 // less than a non-wildcard value. 106 // 107 // For example, the filters: 108 // 109 // "*.*" 110 // "servers.*" 111 // "servers.localhost" 112 // "*.localhost" 113 // 114 // Would be sorted as: 115 // 116 // "servers.localhost" 117 // "servers.*" 118 // "*.localhost" 119 // "*.*" 120 func (n *nodes) Less(j, k int) bool { 121 if (*n)[j].value == "*" && (*n)[k].value != "*" { 122 return false 123 } 124 125 if (*n)[j].value != "*" && (*n)[k].value == "*" { 126 return true 127 } 128 129 return (*n)[j].value < (*n)[k].value 130 } 131 132 // Swap swaps two elements of the array 133 func (n *nodes) Swap(i, j int) { (*n)[i], (*n)[j] = (*n)[j], (*n)[i] } 134 135 // Len returns the length of the array 136 func (n *nodes) Len() int { return len(*n) }