-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtree.go
137 lines (116 loc) · 3.27 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
package gouch
type lookupContext struct {
gouch *Gouch
documentInfoCallback DocumentInfoCallback
walkTreeCallback WalkTreeCallback
indexType int
depth int
callbackContext interface{}
}
type lookupRequest struct {
gouch *Gouch
compare btreeKeyComparator
keys [][]byte
fold bool
inFold bool
fetchCallback callback
nodeCallback callback
callbackContext interface{}
limit int
}
type callback func(req *lookupRequest, key []byte, value []byte) error
func (g *Gouch) btreeLookupInner(req *lookupRequest, diskPos uint64, current, end int) error {
nodeData, err := g.readCompressedDataChunkAt(int64(diskPos))
if err != nil {
return err
}
if nodeData[0] == BtreeInterior {
kvIterator := newKeyValueIterator(nodeData[1:])
context := req.callbackContext.(*lookupContext)
count := context.callbackContext.(map[string]int)["count"]
if count < req.limit {
for k, v := kvIterator.Next(); k != nil && current < end; k, v = kvIterator.Next() {
cmp := req.compare(k, req.keys[current])
if cmp >= 0 {
if req.fold {
req.inFold = true
}
// Descend into the pointed to node.
// with all keys < item key.
lastItem := current + 1
for lastItem < end && req.compare(k, req.keys[lastItem]) >= 0 {
lastItem++
}
if req.nodeCallback != nil {
err = req.nodeCallback(req, k, v)
if err != nil {
return err
}
}
valNodePointer := decodeNodePointer(v)
err = g.btreeLookupInner(req, valNodePointer.pointer, current, lastItem)
if err != nil {
return err
}
if !req.inFold {
current = lastItem
}
if req.nodeCallback != nil {
err = req.nodeCallback(req, nil, nil)
if err != nil {
return err
}
}
}
}
} else {
return nil
}
} else if nodeData[0] == BtreeLeaf {
kvIterator := newKeyValueIterator(nodeData[1:])
context := req.callbackContext.(*lookupContext)
count := context.callbackContext.(map[string]int)["count"]
//look if we already dumped limit KV pairs
if count < req.limit {
for k, v := kvIterator.Next(); k != nil && current < end; k, v = kvIterator.Next() {
count := context.callbackContext.(map[string]int)["count"]
//look if we already dumped limit KV pairs
if count < req.limit {
cmp := req.compare(k, req.keys[current])
if cmp >= 0 && req.fold && !req.inFold {
req.inFold = true
} else if req.inFold && (current+1) < end && req.compare(k, req.keys[current+1]) > 0 {
//We've hit a key past the end of our range.
req.inFold = false
req.fold = false
current = end
}
if cmp == 0 || (cmp > 0 && req.inFold) {
// Found
err = req.fetchCallback(req, k, v)
if err != nil {
return err
}
if !req.inFold {
current++
}
}
} else {
return nil
}
}
} else {
return nil
}
}
//Any remaining items are not found.
for current < end && !req.fold {
err = req.fetchCallback(req, req.keys[current], nil)
current++
}
return nil
}
func (g *Gouch) btreeLookup(req *lookupRequest, rootPointer uint64) error {
req.inFold = false
return g.btreeLookupInner(req, rootPointer, 0, len(req.keys))
}