github.com/Psiphon-Labs/goarista@v0.0.0-20160825065156-d002785f4c67/pathmap/pathmap.go (about) 1 // Copyright (C) 2016 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package pathmap 6 7 // PathMap associates Paths to a values. It allows wildcards. The 8 // primary use of PathMap is to be able to register handlers to paths 9 // that can be efficiently looked up every time a path is updated. 10 // 11 // For example: 12 // 13 // m.Set({"interfaces", "*", "adminStatus"}, AdminStatusHandler) 14 // m.Set({"interface", "Management1", "adminStatus"}, Management1AdminStatusHandler) 15 // 16 // m.Visit({"interfaces", "Ethernet3/32/1", "adminStatus"}, HandlerExecutor) 17 // >> AdminStatusHandler gets passed to HandlerExecutor 18 // m.Visit({"interfaces", "Management1", "adminStatus"}, HandlerExecutor) 19 // >> AdminStatusHandler and Management1AdminStatusHandler gets passed to HandlerExecutor 20 // 21 // Note, Visit performance is typically linearly with the length of 22 // the path. But, it can be as bad as O(2^len(Path)) when TreeMap 23 // nodes have children and a wildcard associated with it. For example, 24 // if these paths were registered: 25 // 26 // m.Set([]string{"foo", "bar", "baz"}, 1) 27 // m.Set([]string{"*", "bar", "baz"}, 2) 28 // m.Set([]string{"*", "*", "baz"}, 3) 29 // m.Set([]string{"*", "*", "*"}, 4) 30 // m.Set([]string{"foo", "*", "*"}, 5) 31 // m.Set([]string{"foo", "bar", "*"}, 6) 32 // m.Set([]string{"foo", "*", "baz"}, 7) 33 // m.Set([]string{"*", "bar", "*"}, 8) 34 // 35 // m.Visit([]{"foo","bar","baz"}, Foo) // 2^3 nodes traversed 36 // 37 // This shouldn't be a concern with our paths because it is likely 38 // that a TreeMap node will either have a wildcard or children, not 39 // both. A TreeMap node that corresponds to a collection will often be a 40 // wildcard, otherwise it will have specific children. 41 type PathMap interface { 42 // Visit calls f for every registration in the PathMap that 43 // matches path. For example, 44 // 45 // m.Set({"foo", "bar"}, 1) 46 // m.Set({"*", "bar"}, 2) 47 // 48 // m.Visit({"foo", "bar"}, Printer) 49 // >> Calls Printer(1) and Printer(2) 50 Visit(path []string, f VisitorFunc) error 51 52 // Get returns the mapping for path. This returns the exact 53 // mapping for path. For example, if you register two paths 54 // 55 // m.Set({"foo", "bar"}, 1) 56 // m.Set({"*", "bar"}, 2) 57 // 58 // m.Get({"foo", "bar"}) => 1 59 // m.Get({"*", "bar"}) => 2 60 Get(path []string) interface{} 61 62 // Set a mapping of path to value. Path may contain wildcards. Set 63 // replaces what was there before. 64 Set(path []string, v interface{}) 65 66 // Delete removes the mapping for path 67 Delete(path []string) bool 68 } 69 70 // Wildcard is a special string representing any possible path 71 const Wildcard string = "*" 72 73 type node struct { 74 val interface{} 75 wildcard *node 76 children map[string]*node 77 } 78 79 // New creates a new PathMap 80 func New() PathMap { 81 return &node{} 82 } 83 84 // VisitorFunc is the func type passed to Visit 85 type VisitorFunc func(v interface{}) error 86 87 // Visit calls f for every matching registration in the PathMap 88 func (n *node) Visit(path []string, f VisitorFunc) error { 89 for i, element := range path { 90 if n.wildcard != nil { 91 if err := n.wildcard.Visit(path[i+1:], f); err != nil { 92 return err 93 } 94 } 95 next, ok := n.children[element] 96 if !ok { 97 return nil 98 } 99 n = next 100 } 101 if n.val == nil { 102 return nil 103 } 104 return f(n.val) 105 } 106 107 // Get returns the mapping for path 108 func (n *node) Get(path []string) interface{} { 109 for _, element := range path { 110 if element == Wildcard { 111 if n.wildcard == nil { 112 return nil 113 } 114 n = n.wildcard 115 continue 116 } 117 next, ok := n.children[element] 118 if !ok { 119 return nil 120 } 121 n = next 122 } 123 return n.val 124 } 125 126 // Set a mapping of path to value. Path may contain wildcards. Set 127 // replaces what was there before. 128 func (n *node) Set(path []string, v interface{}) { 129 for _, element := range path { 130 if element == Wildcard { 131 if n.wildcard == nil { 132 n.wildcard = &node{} 133 } 134 n = n.wildcard 135 continue 136 } 137 if n.children == nil { 138 n.children = map[string]*node{} 139 } 140 next, ok := n.children[element] 141 if !ok { 142 next = &node{} 143 n.children[element] = next 144 } 145 n = next 146 } 147 n.val = v 148 } 149 150 // Delete removes the mapping for path 151 func (n *node) Delete(path []string) bool { 152 nodes := make([]*node, len(path)+1) 153 for i, element := range path { 154 nodes[i] = n 155 if element == Wildcard { 156 if n.wildcard == nil { 157 return false 158 } 159 n = n.wildcard 160 continue 161 } 162 next, ok := n.children[element] 163 if !ok { 164 return false 165 } 166 n = next 167 } 168 n.val = nil 169 nodes[len(path)] = n 170 171 // See if we can delete any node objects 172 for i := len(path); i > 0; i-- { 173 n = nodes[i] 174 if n.val != nil || n.wildcard != nil || len(n.children) > 0 { 175 break 176 } 177 parent := nodes[i-1] 178 element := path[i-1] 179 if element == Wildcard { 180 parent.wildcard = nil 181 } else { 182 delete(parent.children, element) 183 } 184 185 } 186 return true 187 }