bolt/cursor.go

170 lines
5.2 KiB
Go
Raw Normal View History

2014-01-08 15:06:17 +00:00
package bolt
2014-01-30 05:11:46 +00:00
import (
"bytes"
"sort"
)
2014-02-12 21:57:27 +00:00
// Cursor represents an iterator that can traverse over all key/value pairs in a bucket in sorted order.
// Cursors can be obtained from a Transaction and are valid as long as the Transaction is open.
2014-01-13 17:35:04 +00:00
type Cursor struct {
2014-01-30 05:11:46 +00:00
transaction *Transaction
2014-01-30 22:04:56 +00:00
root pgid
2014-02-07 22:03:29 +00:00
stack []pageElementRef
2014-01-09 16:07:10 +00:00
}
2014-02-12 21:57:27 +00:00
// First moves the cursor to the first item in the bucket and returns its key and value.
// If the bucket is empty then a nil key is returned.
func (c *Cursor) First() (key []byte, value []byte) {
2014-02-11 15:41:22 +00:00
if len(c.stack) > 0 {
c.stack = c.stack[:0]
}
c.stack = append(c.stack, pageElementRef{page: c.transaction.page(c.root), index: 0})
c.first()
return c.keyValue()
2014-01-09 16:07:10 +00:00
}
2014-02-12 21:57:27 +00:00
// Next moves the cursor to the next item in the bucket and returns its key and value.
// If the cursor is at the end of the bucket then a nil key returned.
func (c *Cursor) Next() (key []byte, value []byte) {
2014-02-11 15:41:22 +00:00
// 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 i := len(c.stack) - 1; i >= 0; i-- {
elem := &c.stack[i]
if elem.index < elem.page.count-1 {
elem.index++
break
}
c.stack = c.stack[:i]
}
// If we've hit the end then return nil.
if len(c.stack) == 0 {
return nil, nil
}
// Move down the stack to find the first element of the first leaf under this branch.
c.first()
return c.keyValue()
2014-01-27 15:11:54 +00:00
}
2014-02-12 21:57:27 +00:00
// Get moves the cursor to a given key and returns its value.
// If the key does not exist then the cursor is left at the closest key and a nil key is returned.
func (c *Cursor) Get(key []byte) (value []byte) {
2014-01-30 05:11:46 +00:00
// Start from root page and traverse to correct page.
c.stack = c.stack[:0]
c.search(key, c.transaction.page(c.root))
2014-01-31 18:18:51 +00:00
p, index := c.top()
// If the cursor is pointing to the end of page then return nil.
if index == p.count {
return nil
}
2014-01-12 05:51:01 +00:00
2014-01-31 18:18:51 +00:00
// If our target node isn't the same key as what's passed in then return nil.
2014-02-07 22:03:29 +00:00
if !bytes.Equal(key, c.element().key()) {
2014-01-31 18:18:51 +00:00
return nil
}
2014-02-07 22:03:29 +00:00
return c.element().value()
2014-01-27 15:11:54 +00:00
}
2014-02-12 21:57:27 +00:00
// first moves the cursor to the first leaf element under the last page in the stack.
2014-02-11 15:41:22 +00:00
func (c *Cursor) first() {
p := c.stack[len(c.stack)-1].page
for {
// Exit when we hit a leaf page.
2014-02-12 21:57:27 +00:00
if (p.flags & leafPageFlag) != 0 {
2014-02-11 15:41:22 +00:00
break
}
// Keep adding pages pointing to the first element to the stack.
p = c.transaction.page(p.branchPageElement(c.stack[len(c.stack)-1].index).pgid)
c.stack = append(c.stack, pageElementRef{page: p, index: 0})
}
}
2014-02-12 21:57:27 +00:00
// search recursively performs a binary search against a given page until it finds a given key.
2014-01-30 05:11:46 +00:00
func (c *Cursor) search(key []byte, p *page) {
2014-02-12 21:57:27 +00:00
_assert((p.flags&(branchPageFlag|leafPageFlag)) != 0, "invalid page type: "+p.typ())
2014-02-07 22:03:29 +00:00
e := pageElementRef{page: p}
2014-01-30 05:11:46 +00:00
c.stack = append(c.stack, e)
// If we're on a leaf page then find the specific node.
2014-02-12 21:57:27 +00:00
if (p.flags & leafPageFlag) != 0 {
2014-01-30 05:11:46 +00:00
c.nsearch(key, p)
return
}
2014-01-31 18:18:51 +00:00
// Binary search for the correct range.
inodes := p.branchPageElements()
var exact bool
index := sort.Search(int(p.count), func(i int) bool {
// TODO(benbjohnson): Optimize this range search. It's a bit hacky right now.
// sort.Search() finds the lowest index where f() != -1 but we need the highest index.
ret := bytes.Compare(inodes[i].key(), key)
if ret == 0 {
exact = true
}
return ret != -1
})
if !exact && index > 0 {
index--
}
2014-02-07 22:03:29 +00:00
c.stack[len(c.stack)-1].index = uint16(index)
2014-01-30 05:11:46 +00:00
// Recursively search to the next page.
2014-02-07 22:03:29 +00:00
c.search(key, c.transaction.page(inodes[index].pgid))
2014-01-30 05:11:46 +00:00
}
// nsearch searches a leaf node for the index of the node that matches key.
func (c *Cursor) nsearch(key []byte, p *page) {
e := &c.stack[len(c.stack)-1]
// Binary search for the correct leaf node index.
2014-01-31 18:18:51 +00:00
inodes := p.leafPageElements()
index := sort.Search(int(p.count), func(i int) bool {
return bytes.Compare(inodes[i].key(), key) != -1
2014-01-30 05:11:46 +00:00
})
2014-01-31 18:18:51 +00:00
e.index = uint16(index)
2014-01-30 05:11:46 +00:00
}
2014-01-28 03:22:37 +00:00
// top returns the page and leaf node that the cursor is currently pointing at.
2014-01-31 18:18:51 +00:00
func (c *Cursor) top() (*page, uint16) {
2014-02-07 22:03:29 +00:00
ptr := c.stack[len(c.stack)-1]
return ptr.page, ptr.index
2014-01-27 15:11:54 +00:00
}
2014-02-07 22:03:29 +00:00
// element returns the leaf element that the cursor is currently positioned on.
func (c *Cursor) element() *leafPageElement {
ref := c.stack[len(c.stack)-1]
return ref.page.leafPageElement(ref.index)
}
2014-02-11 15:41:22 +00:00
// keyValue returns the key and value of the current leaf element.
func (c *Cursor) keyValue() ([]byte, []byte) {
ref := &c.stack[len(c.stack)-1]
if ref.index >= ref.page.count {
return nil, nil
}
e := ref.page.leafPageElement(ref.index)
return e.key(), e.value()
}
2014-02-07 22:03:29 +00:00
// node returns the node that the cursor is currently positioned on.
func (c *Cursor) node(t *RWTransaction) *node {
2014-02-16 06:38:03 +00:00
_assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack")
2014-02-07 22:03:29 +00:00
// Start from root and traverse down the hierarchy.
n := t.node(c.stack[0].page.id, nil)
for _, ref := range c.stack[:len(c.stack)-1] {
2014-02-08 05:55:25 +00:00
_assert(!n.isLeaf, "expected branch node")
_assert(ref.page.id == n.pgid, "node/page mismatch a: %d != %d", ref.page.id, n.childAt(int(ref.index)).pgid)
n = n.childAt(int(ref.index))
2014-02-07 22:03:29 +00:00
}
2014-02-08 05:55:25 +00:00
_assert(n.isLeaf, "expected leaf node")
_assert(n.pgid == c.stack[len(c.stack)-1].page.id, "node/page mismatch b: %d != %d", n.pgid, c.stack[len(c.stack)-1].page.id)
2014-02-07 22:03:29 +00:00
return n
2014-01-09 16:07:10 +00:00
}