forked from etcd-io/etcd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tree.go
318 lines (256 loc) · 6.38 KB
/
tree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package store
import (
"path"
"sort"
"strings"
"time"
)
//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------
// A file system like tree structure. Each non-leaf node of the tree has a hashmap to
// store its children nodes. Leaf nodes has no hashmap (a nil pointer)
type tree struct {
Root *treeNode
}
// A treeNode wraps a Node. It has a hashmap to keep records of its children treeNodes.
type treeNode struct {
InternalNode Node
Dir bool
NodeMap map[string]*treeNode
}
// TreeNode with its key. We use it when we need to sort the treeNodes.
type tnWithKey struct {
key string
tn *treeNode
}
// Define type and functions to match sort interface
type tnWithKeySlice []tnWithKey
func (s tnWithKeySlice) Len() int { return len(s) }
func (s tnWithKeySlice) Less(i, j int) bool { return s[i].key < s[j].key }
func (s tnWithKeySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// CONSTANT VARIABLE
// Represent an empty node
var emptyNode = Node{"", PERMANENT, nil}
//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------
// Set the key to the given value, return true if success
// If any intermidate path of the key is not a directory type, it will fail
// For example if the /foo = Node(bar) exists, set /foo/foo = Node(barbar)
// will fail.
func (t *tree) set(key string, value Node) bool {
nodesName := split(key)
// avoid set value to "/"
if len(nodesName) == 1 && len(nodesName[0]) == 0 {
return false
}
nodeMap := t.Root.NodeMap
i := 0
newDir := false
// go through all the path
for i = 0; i < len(nodesName)-1; i++ {
// if we meet a new directory, all the directory after it must be new
if newDir {
tn := &treeNode{emptyNode, true, make(map[string]*treeNode)}
nodeMap[nodesName[i]] = tn
nodeMap = tn.NodeMap
continue
}
// get the node from the nodeMap of the current level
tn, ok := nodeMap[nodesName[i]]
if !ok {
// add a new directory and set newDir to true
newDir = true
tn := &treeNode{emptyNode, true, make(map[string]*treeNode)}
nodeMap[nodesName[i]] = tn
nodeMap = tn.NodeMap
} else if ok && !tn.Dir {
// if we meet a non-directory node, we cannot set the key
return false
} else {
// update the nodeMap to next level
nodeMap = tn.NodeMap
}
}
// Add the last node
tn, ok := nodeMap[nodesName[i]]
if !ok {
// we add a new treeNode
tn := &treeNode{value, false, nil}
nodeMap[nodesName[i]] = tn
} else {
if tn.Dir {
return false
}
// we change the value of a old Treenode
tn.InternalNode = value
}
return true
}
// Get the tree node of the key
func (t *tree) internalGet(key string) (*treeNode, bool) {
nodesName := split(key)
// should be able to get root
if len(nodesName) == 1 && nodesName[0] == "" {
return t.Root, true
}
nodeMap := t.Root.NodeMap
var i int
for i = 0; i < len(nodesName)-1; i++ {
node, ok := nodeMap[nodesName[i]]
if !ok || !node.Dir {
return nil, false
}
nodeMap = node.NodeMap
}
tn, ok := nodeMap[nodesName[i]]
if ok {
return tn, ok
} else {
return nil, ok
}
}
// get the internalNode of the key
func (t *tree) get(key string) (Node, bool) {
tn, ok := t.internalGet(key)
if ok {
if tn.Dir {
return emptyNode, false
}
return tn.InternalNode, ok
} else {
return emptyNode, ok
}
}
// get the internalNode of the key
func (t *tree) list(directory string) (interface{}, []string, bool) {
treeNode, ok := t.internalGet(directory)
if !ok {
return nil, nil, ok
} else {
if !treeNode.Dir {
return &treeNode.InternalNode, nil, ok
}
length := len(treeNode.NodeMap)
nodes := make([]*Node, length)
keys := make([]string, length)
i := 0
for key, node := range treeNode.NodeMap {
nodes[i] = &node.InternalNode
keys[i] = key
i++
}
return nodes, keys, ok
}
}
// delete the key, return true if success
func (t *tree) delete(key string) bool {
nodesName := split(key)
nodeMap := t.Root.NodeMap
var i int
for i = 0; i < len(nodesName)-1; i++ {
node, ok := nodeMap[nodesName[i]]
if !ok || !node.Dir {
return false
}
nodeMap = node.NodeMap
}
node, ok := nodeMap[nodesName[i]]
if ok && !node.Dir {
delete(nodeMap, nodesName[i])
return true
}
return false
}
// traverse wrapper
func (t *tree) traverse(f func(string, *Node), sort bool) {
if sort {
sortDfs("", t.Root, f)
} else {
dfs("", t.Root, f)
}
}
// clone() will return a deep cloned tree
func (t *tree) clone() *tree {
newTree := new(tree)
newTree.Root = &treeNode{
Node{
"/",
time.Unix(0, 0),
nil,
},
true,
make(map[string]*treeNode),
}
recursiveClone(t.Root, newTree.Root)
return newTree
}
// recursiveClone is a helper function for clone()
func recursiveClone(tnSrc *treeNode, tnDes *treeNode) {
if !tnSrc.Dir {
tnDes.InternalNode = tnSrc.InternalNode
return
} else {
tnDes.InternalNode = tnSrc.InternalNode
tnDes.Dir = true
tnDes.NodeMap = make(map[string]*treeNode)
for key, tn := range tnSrc.NodeMap {
newTn := new(treeNode)
recursiveClone(tn, newTn)
tnDes.NodeMap[key] = newTn
}
}
}
// deep first search to traverse the tree
// apply the func f to each internal node
func dfs(key string, t *treeNode, f func(string, *Node)) {
// base case
if len(t.NodeMap) == 0 {
f(key, &t.InternalNode)
// recursion
} else {
for tnKey, tn := range t.NodeMap {
tnKey := key + "/" + tnKey
dfs(tnKey, tn, f)
}
}
}
// sort deep first search to traverse the tree
// apply the func f to each internal node
func sortDfs(key string, t *treeNode, f func(string, *Node)) {
// base case
if len(t.NodeMap) == 0 {
f(key, &t.InternalNode)
// recursion
} else {
s := make(tnWithKeySlice, len(t.NodeMap))
i := 0
// copy
for tnKey, tn := range t.NodeMap {
tnKey := key + "/" + tnKey
s[i] = tnWithKey{tnKey, tn}
i++
}
// sort
sort.Sort(s)
// traverse
for i = 0; i < len(t.NodeMap); i++ {
sortDfs(s[i].key, s[i].tn, f)
}
}
}
// split the key by '/', get the intermediate node name
func split(key string) []string {
key = "/" + key
key = path.Clean(key)
// get the intermidate nodes name
nodesName := strings.Split(key, "/")
// we do not need the root node, since we start with it
nodesName = nodesName[1:]
return nodesName
}