293dd2e848
* Add vendor dir so builds dont require dep * Pin specific version go-eth version
270 lines
6.9 KiB
Go
270 lines
6.9 KiB
Go
// Copyright (c) 2017 Arista Networks, Inc.
|
|
// Use of this source code is governed by the Apache License 2.0
|
|
// that can be found in the COPYING file.
|
|
|
|
package path
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/aristanetworks/goarista/key"
|
|
"github.com/aristanetworks/goarista/pathmap"
|
|
)
|
|
|
|
// Map associates Paths to values. It allows wildcards. The
|
|
// primary use of Map is to be able to register handlers to paths
|
|
// that can be efficiently looked up every time a path is updated.
|
|
//
|
|
// For example:
|
|
//
|
|
// m.Set({key.New("interfaces"), key.New("*"), key.New("adminStatus")}, AdminStatusHandler)
|
|
// m.Set({key.New("interface"), key.New("Management1"), key.New("adminStatus")},
|
|
// Management1AdminStatusHandler)
|
|
//
|
|
// m.Visit(Path{key.New("interfaces"), key.New("Ethernet3/32/1"), key.New("adminStatus")},
|
|
// HandlerExecutor)
|
|
// >> AdminStatusHandler gets passed to HandlerExecutor
|
|
// m.Visit(Path{key.New("interfaces"), key.New("Management1"), key.New("adminStatus")},
|
|
// HandlerExecutor)
|
|
// >> AdminStatusHandler and Management1AdminStatusHandler gets passed to HandlerExecutor
|
|
//
|
|
// Note, Visit performance is typically linearly with the length of
|
|
// the path. But, it can be as bad as O(2^len(Path)) when TreeMap
|
|
// nodes have children and a wildcard associated with it. For example,
|
|
// if these paths were registered:
|
|
//
|
|
// m.Set(Path{key.New("foo"), key.New("bar"), key.New("baz")}, 1)
|
|
// m.Set(Path{key.New("*"), key.New("bar"), key.New("baz")}, 2)
|
|
// m.Set(Path{key.New("*"), key.New("*"), key.New("baz")}, 3)
|
|
// m.Set(Path{key.New("*"), key.New("*"), key.New("*")}, 4)
|
|
// m.Set(Path{key.New("foo"), key.New("*"), key.New("*")}, 5)
|
|
// m.Set(Path{key.New("foo"), key.New("bar"), key.New("*")}, 6)
|
|
// m.Set(Path{key.New("foo"), key.New("*"), key.New("baz")}, 7)
|
|
// m.Set(Path{key.New("*"), key.New("bar"), key.New("*")}, 8)
|
|
//
|
|
// m.Visit(Path{key.New("foo"),key.New("bar"),key.New("baz")}, Foo) // 2^3 nodes traversed
|
|
//
|
|
// This shouldn't be a concern with our paths because it is likely
|
|
// that a TreeMap node will either have a wildcard or children, not
|
|
// both. A TreeMap node that corresponds to a collection will often be a
|
|
// wildcard, otherwise it will have specific children.
|
|
type Map interface {
|
|
// Visit calls f for every registration in the Map that
|
|
// matches path. For example,
|
|
//
|
|
// m.Set(Path{key.New("foo"), key.New("bar")}, 1)
|
|
// m.Set(Path{key.New("*"), key.New("bar")}, 2)
|
|
//
|
|
// m.Visit(Path{key.New("foo"), key.New("bar")}, Printer)
|
|
// >> Calls Printer(1) and Printer(2)
|
|
Visit(p Path, f pathmap.VisitorFunc) error
|
|
|
|
// VisitPrefix calls f for every registration in the Map that
|
|
// is a prefix of path. For example,
|
|
//
|
|
// m.Set(Path{}, 0)
|
|
// m.Set(Path{key.New("foo")}, 1)
|
|
// m.Set(Path{key.New("foo"), key.New("bar")}, 2)
|
|
// m.Set(Path{key.New("foo"), key.New("quux")}, 3)
|
|
// m.Set(Path{key.New("*"), key.New("bar")}, 4)
|
|
//
|
|
// m.VisitPrefix(Path{key.New("foo"), key.New("bar"), key.New("baz")}, Printer)
|
|
// >> Calls Printer on values 0, 1, 2, and 4
|
|
VisitPrefix(p Path, f pathmap.VisitorFunc) error
|
|
|
|
// Get returns the mapping for path. This returns the exact
|
|
// mapping for path. For example, if you register two paths
|
|
//
|
|
// m.Set(Path{key.New("foo"), key.New("bar")}, 1)
|
|
// m.Set(Path{key.New("*"), key.New("bar")}, 2)
|
|
//
|
|
// m.Get(Path{key.New("foo"), key.New("bar")}) => 1
|
|
// m.Get(Path{key.New("*"), key.New("bar")}) => 2
|
|
Get(p Path) interface{}
|
|
|
|
// Set a mapping of path to value. Path may contain wildcards. Set
|
|
// replaces what was there before.
|
|
Set(p Path, v interface{})
|
|
|
|
// Delete removes the mapping for path
|
|
Delete(p Path) bool
|
|
}
|
|
|
|
// Wildcard is a special key representing any possible path
|
|
var Wildcard key.Key = key.New("*")
|
|
|
|
type node struct {
|
|
val interface{}
|
|
wildcard *node
|
|
children map[key.Key]*node
|
|
}
|
|
|
|
// NewMap creates a new Map
|
|
func NewMap() Map {
|
|
return &node{}
|
|
}
|
|
|
|
// Visit calls f for every matching registration in the Map
|
|
func (n *node) Visit(p Path, f pathmap.VisitorFunc) error {
|
|
for i, element := range p {
|
|
if n.wildcard != nil {
|
|
if err := n.wildcard.Visit(p[i+1:], f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
next, ok := n.children[element]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
n = next
|
|
}
|
|
if n.val == nil {
|
|
return nil
|
|
}
|
|
return f(n.val)
|
|
}
|
|
|
|
// VisitPrefix calls f for every registered path that is a prefix of
|
|
// the path
|
|
func (n *node) VisitPrefix(p Path, f pathmap.VisitorFunc) error {
|
|
for i, element := range p {
|
|
// Call f on each node we visit
|
|
if n.val != nil {
|
|
if err := f(n.val); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if n.wildcard != nil {
|
|
if err := n.wildcard.VisitPrefix(p[i+1:], f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
next, ok := n.children[element]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
n = next
|
|
}
|
|
if n.val == nil {
|
|
return nil
|
|
}
|
|
// Call f on the final node
|
|
return f(n.val)
|
|
}
|
|
|
|
// Get returns the mapping for path
|
|
func (n *node) Get(p Path) interface{} {
|
|
for _, element := range p {
|
|
if element.Equal(Wildcard) {
|
|
if n.wildcard == nil {
|
|
return nil
|
|
}
|
|
n = n.wildcard
|
|
continue
|
|
}
|
|
next, ok := n.children[element]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
n = next
|
|
}
|
|
return n.val
|
|
}
|
|
|
|
// Set a mapping of path to value. Path may contain wildcards. Set
|
|
// replaces what was there before.
|
|
func (n *node) Set(p Path, v interface{}) {
|
|
for _, element := range p {
|
|
if element.Equal(Wildcard) {
|
|
if n.wildcard == nil {
|
|
n.wildcard = &node{}
|
|
}
|
|
n = n.wildcard
|
|
continue
|
|
}
|
|
if n.children == nil {
|
|
n.children = map[key.Key]*node{}
|
|
}
|
|
next, ok := n.children[element]
|
|
if !ok {
|
|
next = &node{}
|
|
n.children[element] = next
|
|
}
|
|
n = next
|
|
}
|
|
n.val = v
|
|
}
|
|
|
|
// Delete removes the mapping for path
|
|
func (n *node) Delete(p Path) bool {
|
|
nodes := make([]*node, len(p)+1)
|
|
for i, element := range p {
|
|
nodes[i] = n
|
|
if element.Equal(Wildcard) {
|
|
if n.wildcard == nil {
|
|
return false
|
|
}
|
|
n = n.wildcard
|
|
continue
|
|
}
|
|
next, ok := n.children[element]
|
|
if !ok {
|
|
return false
|
|
}
|
|
n = next
|
|
}
|
|
n.val = nil
|
|
nodes[len(p)] = n
|
|
|
|
// See if we can delete any node objects
|
|
for i := len(p); i > 0; i-- {
|
|
n = nodes[i]
|
|
if n.val != nil || n.wildcard != nil || len(n.children) > 0 {
|
|
break
|
|
}
|
|
parent := nodes[i-1]
|
|
element := p[i-1]
|
|
if element.Equal(Wildcard) {
|
|
parent.wildcard = nil
|
|
} else {
|
|
delete(parent.children, element)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (n *node) String() string {
|
|
var b bytes.Buffer
|
|
n.write(&b, "")
|
|
return b.String()
|
|
}
|
|
|
|
func (n *node) write(b *bytes.Buffer, indent string) {
|
|
if n.val != nil {
|
|
b.WriteString(indent)
|
|
fmt.Fprintf(b, "Val: %v", n.val)
|
|
b.WriteString("\n")
|
|
}
|
|
if n.wildcard != nil {
|
|
b.WriteString(indent)
|
|
fmt.Fprintf(b, "Child %q:\n", Wildcard)
|
|
n.wildcard.write(b, indent+" ")
|
|
}
|
|
children := make([]key.Key, 0, len(n.children))
|
|
for key := range n.children {
|
|
children = append(children, key)
|
|
}
|
|
sort.Slice(children, func(i, j int) bool {
|
|
return children[i].String() < children[j].String()
|
|
})
|
|
|
|
for _, key := range children {
|
|
child := n.children[key]
|
|
b.WriteString(indent)
|
|
fmt.Fprintf(b, "Child %q:\n", key.String())
|
|
child.write(b, indent+" ")
|
|
}
|
|
}
|