Skip to content

Commit 08adfdb

Browse files
committed
add ingressHostsTree
1 parent 0b8d4b7 commit 08adfdb

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

pkg/ingressCache/ingressCache.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package ingresscache
2+
3+
type IngressHostsTree interface {
4+
SetFunctionName(path string, function string) error // will overwrite existing values if exists
5+
DeleteFunctionName(path string, function string) error
6+
GetFunctionName(path string) ([]string, error)
7+
}

pkg/ingressCache/pathTrie.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package ingresscache
2+
3+
import (
4+
"fmt"
5+
"github.com/dghubble/trie"
6+
"slices"
7+
8+
"github.com/nuclio/errors"
9+
)
10+
11+
type PathTree struct {
12+
t *trie.PathTrie
13+
}
14+
15+
// NewPathTree creates a new PathTree instance
16+
func NewPathTree() *PathTree {
17+
return &PathTree{trie.NewPathTrie()}
18+
}
19+
20+
// SetFunctionName sets a function for a given path. If the path does not exist, it creates it
21+
func (p *PathTree) SetFunctionName(path string, function string) error {
22+
if path == "" {
23+
return errors.New("path is empty")
24+
}
25+
26+
if function == "" {
27+
return errors.New("function is empty")
28+
}
29+
30+
// get the exact path value in order to avoid creating a new path if it already exists
31+
pathValue := p.t.Get(path)
32+
if pathValue == nil {
33+
p.t.Put(path, []string{function})
34+
return nil
35+
}
36+
37+
pathFunctionNames, ok := pathValue.([]string)
38+
if !ok {
39+
return errors.New(fmt.Sprintf("value is not a []string, got: %T", pathValue))
40+
}
41+
42+
if slices.Contains(pathFunctionNames, function) {
43+
return nil
44+
}
45+
46+
pathFunctionNames = append(pathFunctionNames, function)
47+
p.t.Put(path, pathFunctionNames)
48+
49+
return nil
50+
}
51+
52+
// DeleteFunctionName removes a function from a path and also deletes the path if the function is the only one associated with that path
53+
func (p *PathTree) DeleteFunctionName(path string, function string) error {
54+
pathValue := p.t.Get(path)
55+
if pathValue == nil {
56+
// If pathValue is nil, the path does not exist, so nothing to delete
57+
return nil
58+
}
59+
60+
pathFunctionNames, ok := pathValue.([]string)
61+
if !ok {
62+
return errors.New(fmt.Sprintf("path value should be []string, got %T", pathValue))
63+
}
64+
65+
// If the function is the only value, delete the path
66+
if len(pathFunctionNames) == 1 {
67+
if pathFunctionNames[0] == function {
68+
p.t.Delete(path)
69+
return nil
70+
}
71+
return errors.New(fmt.Sprintf("the function-name doesn't exists in path, skipping delete. function-name: %s, path: %s", function, path))
72+
}
73+
74+
// TODO - will be removed once moving into efficient pathFunctionNames implementation (i.e. not using slices)
75+
pathFunctionNames = excludeElemFromSlice(pathFunctionNames, function)
76+
p.t.Put(path, pathFunctionNames)
77+
return nil
78+
}
79+
80+
// GetFunctionName retrieve the closest prefix matching the path and returns the associated functions
81+
func (p *PathTree) GetFunctionName(path string) ([]string, error) {
82+
var walkPathResult interface{}
83+
if err := p.t.WalkPath(path, func(_ string, value interface{}) error {
84+
if value != nil {
85+
walkPathResult = value
86+
}
87+
88+
return nil
89+
}); err != nil {
90+
return nil, errors.New(fmt.Sprintf("no value found for path: %s", path))
91+
}
92+
93+
output, ok := walkPathResult.([]string)
94+
if !ok {
95+
return nil, errors.New(fmt.Sprintf("value is not a []string, value: %v", walkPathResult))
96+
}
97+
98+
return output, nil
99+
}
100+
101+
// TODO - will be removed once moving into efficient pathFunctionNames implementation (i.e. not using slices)
102+
func excludeElemFromSlice(slice []string, elem string) []string {
103+
// 'j' is the "write" index. It tracks where the next element to keep should be placed.
104+
j := 0
105+
106+
// Iterate through the original slice using 'i' as the "read" index.
107+
for i := 0; i < len(slice); i++ {
108+
// If the current element (s[i]) is NOT the one we want to remove,
109+
// copy it to the current "write" position (s[j]).
110+
if slice[i] != elem {
111+
slice[j] = slice[i]
112+
j++ // Increment the write index, preparing for the next element to keep.
113+
}
114+
}
115+
116+
slice[j] = "" // This helps the garbage collector reclaim memory for the string data
117+
118+
// Return a re-sliced version of 's' up to the new length 'j'.
119+
return slice[:j]
120+
}

0 commit comments

Comments
 (0)