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  }