bolt/cursor.go

114 lines
2.8 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-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
stack []elem
2014-01-09 16:07:10 +00:00
}
2014-01-26 22:29:06 +00:00
// elem represents a node on a page that's on the cursor's stack.
type elem struct {
2014-01-24 23:32:18 +00:00
page *page
2014-01-31 18:18:51 +00:00
index uint16
2014-01-09 16:07:10 +00:00
}
2014-01-26 22:29:06 +00:00
// First moves the cursor to the first item in the bucket and returns its key and data.
2014-01-27 15:11:54 +00:00
func (c *Cursor) First() ([]byte, []byte) {
2014-01-26 22:29:06 +00:00
// TODO: Traverse to the first key.
2014-01-27 15:11:54 +00:00
return nil, nil
2014-01-09 16:07:10 +00:00
}
2014-01-24 23:32:18 +00:00
// Move the cursor to the next key/value.
2014-01-27 15:11:54 +00:00
func (c *Cursor) Next() ([]byte, []byte) {
return nil, nil
}
// Get positions the cursor at a specific key and returns the its value.
func (c *Cursor) Get(key []byte) []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.
if !bytes.Equal(key, c.node().key()) {
return nil
}
return c.node().value()
2014-01-27 15:11:54 +00:00
}
2014-01-30 05:11:46 +00:00
func (c *Cursor) search(key []byte, p *page) {
2014-01-31 18:18:51 +00:00
if (p.flags & (p_branch | p_leaf)) == 0 {
panic("invalid page type: " + p.typ())
}
2014-01-30 05:11:46 +00:00
e := elem{page: p}
c.stack = append(c.stack, e)
// If we're on a leaf page then find the specific node.
if (p.flags & p_leaf) != 0 {
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--
}
e.index = uint16(index)
2014-01-30 05:11:46 +00:00
// Recursively search to the next page.
2014-01-31 18:18:51 +00:00
c.search(key, c.transaction.page(inodes[e.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-01-27 15:11:54 +00:00
elem := c.stack[len(c.stack)-1]
2014-01-31 18:18:51 +00:00
return elem.page, elem.index
2014-01-27 15:11:54 +00:00
}
2014-01-28 03:22:37 +00:00
// page returns the page that the cursor is currently pointing at.
func (c *Cursor) page() *page {
return c.stack[len(c.stack)-1].page
}
2014-01-27 15:11:54 +00:00
// node returns the leaf node that the cursor is currently positioned on.
2014-01-31 18:18:51 +00:00
func (c *Cursor) node() *leafPageElement {
2014-01-27 15:11:54 +00:00
elem := c.stack[len(c.stack)-1]
2014-01-31 18:18:51 +00:00
return elem.page.leafPageElement(elem.index)
2014-01-09 16:07:10 +00:00
}