-
Notifications
You must be signed in to change notification settings - Fork 49
/
iterator.go
222 lines (158 loc) · 4.98 KB
/
iterator.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
/*
* EliasDB
*
* Copyright 2016 Matthias Ladkau. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package hash
import (
"errors"
"fmt"
"github.com/krotik/eliasdb/storage"
)
/*
ErrNoMoreItems is assigned to LastError when Next() is called and there are no
more items to iterate.
*/
var ErrNoMoreItems = errors.New("No more items to iterate")
/*
HTreeIterator data structure
*/
type HTreeIterator struct {
tree *HTree // Tree to iterate
nodePath []uint64 // Path in the tree we currently traversing
indices []int // List of the current indices in the current path
nextKey []byte // Next iterator key (overwritten by nextItem)
nextValue interface{} // Next iterator value
LastError error // Last encountered error
}
/*
NewHTreeIterator creates a new HTreeIterator.
*/
func NewHTreeIterator(tree *HTree) *HTreeIterator {
it := &HTreeIterator{tree, make([]uint64, 0), make([]int, 0), nil, nil, nil}
it.nodePath = append(it.nodePath, tree.Root.Location())
it.indices = append(it.indices, -1)
// Set the nextKey and nextValue properties
it.Next()
return it
}
/*
HasNext returns if there is a next key / value pair.
*/
func (it *HTreeIterator) HasNext() bool {
return it.nextKey != nil
}
/*
Next returns the next key / value pair.
*/
func (it *HTreeIterator) Next() ([]byte, interface{}) {
key := it.nextKey
value := it.nextValue
if err := it.nextItem(); err != ErrNoMoreItems && err != nil {
it.LastError = err
// There was a serious error terminate the iterator
it.nodePath = make([]uint64, 0)
it.indices = make([]int, 0)
it.nextKey = nil
it.nextValue = nil
}
return key, value
}
/*
Retrieve the next key / value pair for the iterator. The tree might
have changed significantly after the last call. We need to cope
with errors as best as we can.
*/
func (it *HTreeIterator) nextItem() error {
// Check if there are more items available to iterate
if len(it.nodePath) == 0 {
it.nextKey = nil
it.nextValue = nil
return ErrNoMoreItems
}
// Get the current path element
loc := it.nodePath[len(it.nodePath)-1]
index := it.indices[len(it.indices)-1]
node, err := it.tree.Root.fetchNode(loc)
if err != nil {
if smr, ok := err.(*storage.ManagerError); ok && smr.Type == storage.ErrSlotNotFound {
// Something is wrong - the tree must have changed since the last
// nextItem call. Remove the path element and try again.
it.nodePath = it.nodePath[:len(it.nodePath)-1]
it.indices = it.indices[:len(it.indices)-1]
return it.nextItem()
}
// If it is another error there is something more serious - report it
return err
}
if node.Children != nil {
// If the current path element is a page get the next child and delegate
page := &htreePage{node}
page.loc = loc
page.sm = it.tree.Root.sm
nextChild := it.searchNextChild(page, index)
if nextChild != -1 {
// If we found another element then update the current index and delegate to it
it.indices[len(it.indices)-1] = nextChild
it.nodePath = append(it.nodePath, page.Children[nextChild])
it.indices = append(it.indices, -1)
return it.nextItem()
}
// If we finished this page remove it from the stack and continue
// with the parent
it.nodePath = it.nodePath[:len(it.nodePath)-1]
it.indices = it.indices[:len(it.indices)-1]
return it.nextItem()
}
// If the current path element is a bucket just iterate the elements
// delegate once it has finished
bucket := &htreeBucket{node}
bucket.loc = loc
bucket.sm = it.tree.Root.sm
nextElement := it.searchNextElement(bucket, index)
if nextElement != -1 {
// If we found another element then update the current index and return it
it.indices[len(it.indices)-1] = nextElement
it.nextKey = bucket.Keys[nextElement]
it.nextValue = bucket.Values[nextElement]
return nil
}
// If we finished this bucket remove it from the stack and continue
// with the parent
it.nodePath = it.nodePath[:len(it.nodePath)-1]
it.indices = it.indices[:len(it.indices)-1]
return it.nextItem()
}
/*
searchNextChild searches for the index of the next available page child from a given index.
*/
func (it *HTreeIterator) searchNextChild(page *htreePage, current int) int {
for i := current + 1; i < MaxPageChildren; i++ {
child := page.Children[i]
if child != 0 {
return i
}
}
return -1
}
/*
searchNextElement searches for the index of the next available bucket element from a given index.
*/
func (it *HTreeIterator) searchNextElement(bucket *htreeBucket, current int) int {
next := current + 1
if next < int(bucket.BucketSize) {
return next
}
return -1
}
/*
Return a string representation of the iterator.
*/
func (it *HTreeIterator) String() string {
return fmt.Sprintf("HTree Iterator (tree: %v)\n path: %v\n indices: %v\n next: %v / %v\n",
it.tree.Root.Location(), it.nodePath, it.indices, it.nextKey, it.nextValue)
}