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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
package c
/*
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
// This represents the maximum number of levels that a cursor can traverse.
#define MAX_DEPTH 100
// These flags mark the type of page and are set in the page.flags.
#define PAGE_BRANCH 0x01
#define PAGE_LEAF 0x02
#define PAGE_META 0x04
#define PAGE_FREELIST 0x10
//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
// These types MUST have the same layout as their corresponding Go types
typedef int64_t pgid;
// Page represents a header struct of a block in the mmap.
typedef struct page {
pgid id;
uint16_t flags;
uint16_t count;
uint32_t overflow;
} page;
// The branch element represents an a item in a branch page
// that points to a child page.
typedef struct branch_element {
uint32_t pos;
uint32_t ksize;
pgid pgid;
} branch_element;
// The leaf element represents an a item in a leaf page
// that points to a key/value pair.
typedef struct leaf_element {
uint32_t flags;
uint32_t pos;
uint32_t ksize;
uint32_t vsize;
} leaf_element;
// elem_ref represents a pointer to an element inside of a page.
// It is used by the cursor stack to track the position at each level.
typedef struct elem_ref {
page *page;
uint16_t index;
} elem_ref;
// bolt_val represents a pointer to a fixed-length series of bytes.
// It is used to represent keys and values returned by the cursor.
typedef struct bolt_val {
uint32_t size;
void *data;
} bolt_val;
// bolt_cursor represents a cursor attached to a bucket.
typedef struct bolt_cursor {
void *data;
pgid root;
size_t pgsz;
int top;
elem_ref stack[MAX_DEPTH];
} bolt_cursor;
//------------------------------------------------------------------------------
// Forward Declarations
//------------------------------------------------------------------------------
elem_ref *cursor_push(bolt_cursor *c, pgid id);
elem_ref *cursor_current(bolt_cursor *c);
elem_ref *cursor_pop(bolt_cursor *c);
void cursor_first_leaf(bolt_cursor *c);
void cursor_key_value(bolt_cursor *c, bolt_val *key, bolt_val *value, uint32_t *flags);
void cursor_search(bolt_cursor *c, bolt_val key, pgid id);
void cursor_search_branch(bolt_cursor *c, bolt_val key);
void cursor_search_leaf(bolt_cursor *c, bolt_val key);
//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------
// Initializes a cursor.
void bolt_cursor_init(bolt_cursor *c, void *data, size_t pgsz, pgid root) {
c->data = data;
c->root = root;
c->pgsz = pgsz;
c->top = -1;
}
// Positions the cursor to the first leaf element and returns the key/value pair.
void bolt_cursor_first(bolt_cursor *c, bolt_val *key, bolt_val *value, uint32_t *flags) {
// reset stack to initial state
elem_ref *ref = cursor_push(c, c->root);
// Find first leaf and return key/value.
cursor_first_leaf(c);
cursor_key_value(c, key, value, flags);
}
// Positions the cursor to the next leaf element and returns the key/value pair.
void bolt_cursor_next(bolt_cursor *c, bolt_val *key, bolt_val *value, uint32_t *flags) {
int i;
elem_ref *ref;
// Attempt to move over one element until we're successful.
// Move up the stack as we hit the end of each page in our stack.
for (ref = cursor_current(c); ref != NULL; ref = cursor_current(c)) {
ref->index++;
if (ref->index < ref->page->count) break;
cursor_pop(c);
};
// If we are at the top of the stack then return a blank key/value pair.
if (ref == NULL) {
key->size = value->size = 0;
key->data = value->data = NULL;
*flags = 0;
return;
};
// Find first leaf and return key/value.
cursor_first_leaf(c);
cursor_key_value(c, key, value, flags);
}
// Positions the cursor first leaf element starting from a given key.
// If there is a matching key then the cursor will be place on that key.
// If there not a match then the cursor will be placed on the next key, if available.
void bolt_cursor_seek(bolt_cursor *c, bolt_val seek, bolt_val *key, bolt_val *value, uint32_t *flags) {
// Start from root page/node and traverse to correct page.
cursor_push(c, c->root);
if (seek.size > 0) cursor_search(c, seek, c->root);
elem_ref *ref = cursor_current(c);
// If the cursor is pointing to the end of page then return nil.
if (ref == NULL) {
key->size = value->size = 0;
key->data = value->data = NULL;
*flags = 0;
return;
};
// Find first leaf and return key/value.
cursor_first_leaf(c);
cursor_key_value(c, key, value, flags);
}
//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------
// Push ref to the first element of the page onto the cursor stack
// If the page is the root page reset the stack to initial state
elem_ref *cursor_push(bolt_cursor *c, pgid id) {
elem_ref *ref;
if (id == c->root)
c->top = 0;
else
c->top++;
ref = &(c->stack[c->top]);
ref->page = (page *)(c->data + (c->pgsz * id));
ref->index = 0;
return ref;
}
// Return current element ref from the cursor stack
// If stack is empty return null
elem_ref *cursor_current(bolt_cursor *c) {
if (c->top < 0) return NULL;
return &c->stack[c->top];
}
// Pop current element ref off the cursor stack
elem_ref *cursor_pop(bolt_cursor *c) {
elem_ref *ref = cursor_current(c);
if (ref != NULL) c->top--;
return ref;
}
// Returns the branch element at a given index on a given page.
branch_element *page_branch_element(page *p, uint16_t index) {
branch_element *elements = (branch_element*)((void*)(p) + sizeof(page));
return &elements[index];
}
// Returns the leaf element at a given index on a given page.
leaf_element *page_leaf_element(page *p, uint16_t index) {
leaf_element *elements = (leaf_element*)((void*)(p) + sizeof(page));
return &elements[index];
}
// Returns the key/value pair for the current position of the cursor.
void cursor_key_value(bolt_cursor *c, bolt_val *key, bolt_val *value, uint32_t *flags) {
elem_ref *ref = cursor_current(c);
leaf_element *elem = page_leaf_element(ref->page,ref->index);
// Assign key pointer.
key->size = elem->ksize;
key->data = ((void*)elem) + elem->pos;
// Assign value pointer.
value->size = elem->vsize;
value->data = key->data + key->size;
// Return the element flags.
*flags = elem->flags;
}
// Traverses from the current stack position down to the first leaf element.
void cursor_first_leaf(bolt_cursor *c) {
elem_ref *ref = cursor_current(c);
while (ref->page->flags & PAGE_BRANCH) {
branch_element *elem = page_branch_element(ref->page,ref->index);
ref = cursor_push(c, elem->pgid);
};
}
// Recursively performs a binary search against a given page/node until it finds a given key.
void cursor_search(bolt_cursor *c, bolt_val key, pgid id) {
// Push page onto the cursor stack.
elem_ref *ref = cursor_push(c, id);
// If we're on a leaf page/node then find the specific node.
if (ref->page->flags & PAGE_LEAF) {
cursor_search_leaf(c, key);
return;
}
// Otherwise search the branch page.
cursor_search_branch(c, key);
}
// Recursively search over a leaf page for a key.
void cursor_search_leaf(bolt_cursor *c, bolt_val key) {
elem_ref *ref = cursor_current(c);
int i;
// HACK: Simply loop over elements to find the right one. Replace with a binary search.
leaf_element *elems = (leaf_element*)((void*)(ref->page) + sizeof(page));
for (i=0; i<ref->page->count; i++) {
leaf_element *elem = &elems[i];
int rc = memcmp(key.data, ((void*)elem) + elem->pos, (elem->ksize < key.size ? elem->ksize : key.size));
// printf("? %.*s | %.*s\n", key.size, key.data, elem->ksize, ((void*)elem) + elem->pos);
// printf("rc=%d; key.size(%d) >= elem->ksize(%d)\n", rc, key.size, elem->ksize);
if ((rc == 0 && key.size >= elem->ksize) || rc < 0) {
ref->index = i;
return;
}
}
// If nothing was greater than the key then pop the current page off the stack.
cursor_pop(c);
}
// Recursively search over a branch page for a key.
void cursor_search_branch(bolt_cursor *c, bolt_val key) {
elem_ref *ref = cursor_current(c);
int i;
// HACK: Simply loop over elements to find the right one. Replace with a binary search.
branch_element *elems = (branch_element*)((void*)(ref->page) + sizeof(page));
for (i=0; i<ref->page->count; i++) {
branch_element *elem = &elems[i];
int rc = memcmp(key.data, ((void*)elem) + elem->pos, (elem->ksize < key.size ? elem->ksize : key.size));
if (rc == 0 && key.size == elem->ksize) {
// exact match, done
ref->index = i;
return;
} else if ((rc == 0 && key.size < elem->ksize) || rc < 0) {
// if key is less than anything in this subtree we are done
if (i == 0) return;
// otherwise search the previous subtree
cursor_search(c, key, elems[i-1].pgid);
// didn't find anything greater than key?
if (cursor_current(c) == ref)
ref->index = i;
else
ref->index = i-1;
return;
}
}
// If nothing was greater than the key then pop the current page off the stack.
cursor_pop(c);
}
*/
import "C"
import (
"fmt"
"os"
"unsafe"
"github.com/boltdb/bolt"
)
// Cursor represents a wrapper around a Bolt C cursor.
type Cursor struct {
C *C.bolt_cursor
}
// NewCursor creates a C cursor from a Bucket.
func NewCursor(b *bolt.Bucket) *Cursor {
info := b.Tx().DB().Info()
root := b.Root()
c := &Cursor{C: new(C.bolt_cursor)}
C.bolt_cursor_init(c.C, unsafe.Pointer(&info.Data[0]), C.size_t(info.PageSize), C.pgid(root))
return c
}
// Next moves the cursor to the first element and returns the key and value.
// Returns a nil key if there are no elements.
func (c *Cursor) First() (key, value []byte) {
var k, v C.bolt_val
var flags C.uint32_t
C.bolt_cursor_first(c.C, &k, &v, &flags)
return C.GoBytes(k.data, C.int(k.size)), C.GoBytes(v.data, C.int(v.size))
}
// Next moves the cursor to the next element and returns the key and value.
// Returns a nil key if there are no more key/value pairs.
func (c *Cursor) Next() (key, value []byte) {
var k, v C.bolt_val
var flags C.uint32_t
C.bolt_cursor_next(c.C, &k, &v, &flags)
return C.GoBytes(k.data, C.int(k.size)), C.GoBytes(v.data, C.int(v.size))
}
// Seek moves the cursor to a given key and returns it.
// If the key does not exist then the next key is used. If no keys
// follow, an empty value is returned.
func (c *Cursor) Seek(seek []byte) (key, value []byte, flags int) {
var _flags C.uint32_t
var _seek, k, v C.bolt_val
if len(seek) > 0 {
_seek.size = C.uint32_t(len(seek))
_seek.data = unsafe.Pointer(&seek[0])
}
C.bolt_cursor_seek(c.C, _seek, &k, &v, &_flags)
//fmt.Printf("Key %v [%v]\n", k.data, k.size)
//fmt.Printf("Value %v [%v]\n", k.data, k.size)
if k.data == nil {
return nil, nil, 0
}
return C.GoBytes(k.data, C.int(k.size)), C.GoBytes(v.data, C.int(v.size)), int(_flags)
}
func warn(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
}
func warnf(msg string, v ...interface{}) {
fmt.Fprintf(os.Stderr, msg+"\n", v...)
}
|