github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/path/map.go (about) 1 // Copyright (c) 2017 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 path 6 7 import ( 8 "fmt" 9 "sort" 10 "strings" 11 12 "github.com/aristanetworks/goarista/key" 13 "github.com/aristanetworks/gomap" 14 ) 15 16 // Map associates paths to values. It allows wildcards. A Map 17 // is primarily used to register handlers with paths that can 18 // be easily looked up each time a path is updated. 19 type Map = MapOf[any] 20 21 // VisitorFunc is a function that handles the value associated 22 // with a path in a Map. Note that only the value is passed in 23 // as an argument since the path can be stored inside the value 24 // if needed. 25 type VisitorFunc func(v any) error 26 27 type visitType int 28 29 const ( 30 match visitType = iota 31 prefix 32 suffix 33 children 34 ) 35 36 // MapOf associates paths to values of type T. It allows wildcards. A 37 // Map is primarily used to register handlers with paths that can be 38 // easily looked up each time a path is updated. 39 type MapOf[T any] struct { 40 val T 41 ok bool 42 wildcard *MapOf[T] 43 children *gomap.Map[key.Key, *MapOf[T]] 44 } 45 46 // Visit calls a function fn for every value in the Map 47 // that is registered with a match of a path p. In the 48 // general case, time complexity is linear with respect 49 // to the length of p but it can be as bad as O(2^len(p)) 50 // if there are a lot of paths with wildcards registered. 51 func (m *MapOf[T]) Visit(p key.Path, fn func(v T) error) error { 52 return m.visit(match, p, fn) 53 } 54 55 // VisitPrefixes calls a function fn for every value in the 56 // Map that is registered with a prefix of a path p. 57 func (m *MapOf[T]) VisitPrefixes(p key.Path, fn func(v T) error) error { 58 return m.visit(prefix, p, fn) 59 } 60 61 // VisitPrefixed calls fn for every value in the map that is 62 // registerd with a path that is prefixed by p. This method 63 // can be used to visit every registered path if p is the 64 // empty path (or root path) which prefixes all paths. 65 func (m *MapOf[T]) VisitPrefixed(p key.Path, fn func(v T) error) error { 66 return m.visit(suffix, p, fn) 67 } 68 69 // VisitChildren calls fn for every child for every node that 70 // matches the provided path. 71 func (m *MapOf[T]) VisitChildren(p key.Path, fn func(v T) error) error { 72 return m.visit(children, p, fn) 73 } 74 75 func (m *MapOf[T]) visit(typ visitType, p key.Path, fn func(v T) error) error { 76 for i, element := range p { 77 if m.ok && typ == prefix { 78 if err := fn(m.val); err != nil { 79 return err 80 } 81 } 82 if m.wildcard != nil { 83 if err := m.wildcard.visit(typ, p[i+1:], fn); err != nil { 84 return err 85 } 86 } 87 next, ok := m.children.Get(element) 88 if !ok { 89 return nil 90 } 91 m = next 92 } 93 if typ == children { 94 for it := m.children.Iter(); it.Next(); { 95 if it.Elem().ok { 96 if err := fn(it.Elem().val); err != nil { 97 return err 98 } 99 } 100 } 101 return nil 102 } 103 if typ == suffix { 104 return m.visitSubtree(fn) 105 } 106 if !m.ok { 107 return nil 108 } 109 return fn(m.val) 110 } 111 112 func (m *MapOf[T]) visitSubtree(fn func(v T) error) error { 113 if m.ok { 114 if err := fn(m.val); err != nil { 115 return err 116 } 117 } 118 if m.wildcard != nil { 119 if err := m.wildcard.visitSubtree(fn); err != nil { 120 return err 121 } 122 } 123 for it := m.children.Iter(); it.Next(); { 124 if err := it.Elem().visitSubtree(fn); err != nil { 125 return err 126 } 127 } 128 return nil 129 } 130 131 // IsEmpty returns true if no paths have been registered, false otherwise. 132 func (m *MapOf[T]) IsEmpty() bool { 133 return m.wildcard == nil && m.children.Len() == 0 && !m.ok 134 } 135 136 // Get returns the value registered with an exact match of a path p. 137 // If there is no exact match for p, Get returns the zero value and false. 138 // If p has an exact match and it is set to true, Get 139 // returns its value and true. 140 func (m *MapOf[T]) Get(p key.Path) (T, bool) { 141 var zeroT T 142 for _, element := range p { 143 if element.Equal(Wildcard) { 144 if m.wildcard == nil { 145 return zeroT, false 146 } 147 m = m.wildcard 148 continue 149 } 150 next, ok := m.children.Get(element) 151 if !ok { 152 return zeroT, false 153 } 154 m = next 155 } 156 return m.val, m.ok 157 } 158 159 // GetLongestPrefix determines the longest prefix of p for which an entry exists 160 // within the path map. If such a prefix exists, this function returns the prefix 161 // path, its associated value, and true. Otherwise, this functions returns the empty 162 // path, the zero value of T, and false. 163 func (m *MapOf[T]) GetLongestPrefix(p key.Path) (key.Path, T, bool) { 164 foundPrefixEntry := m.ok 165 prefixEntryPathLen := 0 166 prefixEntryNode := m 167 168 for i, element := range p { 169 next, existsNode := m.children.Get(element) 170 if !existsNode { 171 // Next path element from p does not have an associated map node; return 172 // values corresponding to the longest prefix (with an entry in the map) 173 // visited thus far. 174 break 175 } 176 177 if next.ok { 178 // Found a new entry with a longer prefix; record the details for returning 179 // after the loop. 180 foundPrefixEntry = true 181 prefixEntryPathLen = i + 1 182 prefixEntryNode = next 183 } 184 185 m = next 186 } 187 188 return p[:prefixEntryPathLen], prefixEntryNode.val, foundPrefixEntry 189 } 190 191 func newKeyMap[T any]() *gomap.Map[key.Key, *MapOf[T]] { 192 return gomap.New[key.Key, *MapOf[T]](func(a, b key.Key) bool { return a.Equal(b) }, key.Hash) 193 } 194 195 // Set registers a path p with a value. If the path was already 196 // registered with a value it returns false and true otherwise. 197 func (m *MapOf[T]) Set(p key.Path, v T) bool { 198 for _, element := range p { 199 if element.Equal(Wildcard) { 200 if m.wildcard == nil { 201 m.wildcard = &MapOf[T]{} 202 } 203 m = m.wildcard 204 continue 205 } 206 if m.children == nil { 207 m.children = newKeyMap[T]() 208 } 209 next, ok := m.children.Get(element) 210 if !ok { 211 next = &MapOf[T]{} 212 m.children.Set(element, next) 213 } 214 m = next 215 } 216 set := !m.ok 217 m.val, m.ok = v, true 218 return set 219 } 220 221 // Delete unregisters the value registered with a path. It 222 // returns true if a value was deleted and false otherwise. 223 func (m *MapOf[T]) Delete(p key.Path) bool { 224 maps := make([]*MapOf[T], len(p)+1) 225 for i, element := range p { 226 maps[i] = m 227 if element.Equal(Wildcard) { 228 if m.wildcard == nil { 229 return false 230 } 231 m = m.wildcard 232 continue 233 } 234 next, ok := m.children.Get(element) 235 if !ok { 236 return false 237 } 238 m = next 239 } 240 deleted := m.ok 241 var zeroT T 242 m.val, m.ok = zeroT, false 243 maps[len(p)] = m 244 245 // Remove any empty maps. 246 for i := len(p); i > 0; i-- { 247 m = maps[i] 248 if m.ok || m.wildcard != nil || m.children.Len() > 0 { 249 break 250 } 251 parent := maps[i-1] 252 element := p[i-1] 253 if element.Equal(Wildcard) { 254 parent.wildcard = nil 255 } else { 256 parent.children.Delete(element) 257 } 258 } 259 return deleted 260 } 261 262 func (m *MapOf[T]) String() string { 263 var b strings.Builder 264 m.write(&b, "") 265 return b.String() 266 } 267 268 func (m *MapOf[T]) write(b *strings.Builder, indent string) { 269 if m.ok { 270 b.WriteString(indent) 271 fmt.Fprintf(b, "Val: %v", m.val) 272 b.WriteString("\n") 273 } 274 if m.wildcard != nil { 275 b.WriteString(indent) 276 fmt.Fprintf(b, "Child %q:\n", Wildcard) 277 m.wildcard.write(b, indent+" ") 278 } 279 children := make([]key.Key, 0, m.children.Len()) 280 for it := m.children.Iter(); it.Next(); { 281 children = append(children, it.Key()) 282 } 283 sort.Slice(children, func(i, j int) bool { 284 return children[i].String() < children[j].String() 285 }) 286 287 for _, key := range children { 288 child, _ := m.children.Get(key) 289 b.WriteString(indent) 290 fmt.Fprintf(b, "Child %q:\n", key.String()) 291 child.write(b, indent+" ") 292 } 293 }