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.
|
2014-05-05 01:32:59 +00:00
|
|
|
// Cursors see nested buckets with value == nil.
|
2014-03-09 00:01:49 +00:00
|
|
|
// 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-04-07 22:24:51 +00:00
|
|
|
bucket *Bucket
|
|
|
|
stack []elemRef
|
2014-01-09 16:07:10 +00:00
|
|
|
}
|
|
|
|
|
2014-04-16 13:38:53 +00:00
|
|
|
// Bucket returns the bucket that this cursor was created from.
|
|
|
|
func (c *Cursor) Bucket() *Bucket {
|
|
|
|
return c.bucket
|
|
|
|
}
|
|
|
|
|
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.
|
2014-02-20 18:04:46 +00:00
|
|
|
// If the bucket is empty then a nil key and value are returned.
|
2014-02-12 21:57:27 +00:00
|
|
|
func (c *Cursor) First() (key []byte, value []byte) {
|
2014-04-07 22:24:51 +00:00
|
|
|
_assert(c.bucket.tx.db != nil, "tx closed")
|
2014-02-28 22:13:00 +00:00
|
|
|
c.stack = c.stack[:0]
|
2014-04-07 22:24:51 +00:00
|
|
|
p, n := c.bucket.pageNode(c.bucket.root)
|
2014-03-01 16:13:59 +00:00
|
|
|
c.stack = append(c.stack, elemRef{page: p, node: n, index: 0})
|
2014-02-11 15:41:22 +00:00
|
|
|
c.first()
|
2014-04-07 22:24:51 +00:00
|
|
|
k, v, flags := c.keyValue()
|
|
|
|
if (flags & uint32(bucketLeafFlag)) != 0 {
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
return k, v
|
|
|
|
|
2014-01-09 16:07:10 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 20:53:40 +00:00
|
|
|
// Last moves the cursor to the last item in the bucket and returns its key and value.
|
|
|
|
// If the bucket is empty then a nil key and value are returned.
|
|
|
|
func (c *Cursor) Last() (key []byte, value []byte) {
|
2014-04-07 22:24:51 +00:00
|
|
|
_assert(c.bucket.tx.db != nil, "tx closed")
|
2014-02-28 22:13:00 +00:00
|
|
|
c.stack = c.stack[:0]
|
2014-04-07 22:24:51 +00:00
|
|
|
p, n := c.bucket.pageNode(c.bucket.root)
|
2014-03-01 16:13:59 +00:00
|
|
|
ref := elemRef{page: p, node: n}
|
|
|
|
ref.index = ref.count() - 1
|
|
|
|
c.stack = append(c.stack, ref)
|
2014-02-20 20:53:40 +00:00
|
|
|
c.last()
|
2014-04-07 22:24:51 +00:00
|
|
|
k, v, flags := c.keyValue()
|
|
|
|
if (flags & uint32(bucketLeafFlag)) != 0 {
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
return k, v
|
2014-02-20 20:53:40 +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.
|
2014-02-20 18:04:46 +00:00
|
|
|
// If the cursor is at the end of the bucket then a nil key and value are returned.
|
2014-02-12 21:57:27 +00:00
|
|
|
func (c *Cursor) Next() (key []byte, value []byte) {
|
2014-04-07 22:24:51 +00:00
|
|
|
_assert(c.bucket.tx.db != nil, "tx closed")
|
2014-06-09 18:37:07 +00:00
|
|
|
k, v, flags := c.next()
|
2014-04-07 22:24:51 +00:00
|
|
|
if (flags & uint32(bucketLeafFlag)) != 0 {
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
return k, v
|
2014-01-27 15:11:54 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 20:53:40 +00:00
|
|
|
// Prev moves the cursor to the previous item in the bucket and returns its key and value.
|
|
|
|
// If the cursor is at the beginning of the bucket then a nil key and value are returned.
|
|
|
|
func (c *Cursor) Prev() (key []byte, value []byte) {
|
2014-04-07 22:24:51 +00:00
|
|
|
_assert(c.bucket.tx.db != nil, "tx closed")
|
2014-03-23 18:17:30 +00:00
|
|
|
|
2014-02-20 20:53:40 +00:00
|
|
|
// Attempt to move back one element until we're successful.
|
|
|
|
// Move up the stack as we hit the beginning of each page in our stack.
|
|
|
|
for i := len(c.stack) - 1; i >= 0; i-- {
|
|
|
|
elem := &c.stack[i]
|
|
|
|
if elem.index > 0 {
|
|
|
|
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 last element of the last leaf under this branch.
|
|
|
|
c.last()
|
2014-04-07 22:24:51 +00:00
|
|
|
k, v, flags := c.keyValue()
|
|
|
|
if (flags & uint32(bucketLeafFlag)) != 0 {
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
return k, v
|
2014-02-20 20:53:40 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 18:55:04 +00:00
|
|
|
// 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
|
2014-05-05 01:30:30 +00:00
|
|
|
// follow, a nil key is returned.
|
2014-02-20 18:55:04 +00:00
|
|
|
func (c *Cursor) Seek(seek []byte) (key []byte, value []byte) {
|
2014-04-07 22:24:51 +00:00
|
|
|
k, v, flags := c.seek(seek)
|
2014-06-09 17:07:25 +00:00
|
|
|
|
|
|
|
// If we ended up after the last element of a page then move to the next one.
|
|
|
|
if ref := &c.stack[len(c.stack)-1]; ref.index >= ref.count() {
|
|
|
|
k, v, flags = c.next()
|
|
|
|
}
|
|
|
|
|
2014-04-07 22:24:51 +00:00
|
|
|
if k == nil {
|
|
|
|
return nil, nil
|
|
|
|
} else if (flags & uint32(bucketLeafFlag)) != 0 {
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
return k, v
|
|
|
|
}
|
|
|
|
|
|
|
|
// seek moves the cursor to a given key and returns it.
|
2014-06-09 17:07:25 +00:00
|
|
|
// If the key does not exist then the next key is used.
|
2014-04-07 22:24:51 +00:00
|
|
|
func (c *Cursor) seek(seek []byte) (key []byte, value []byte, flags uint32) {
|
|
|
|
_assert(c.bucket.tx.db != nil, "tx closed")
|
2014-03-23 18:17:30 +00:00
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// Start from root page/node and traverse to correct page.
|
2014-01-30 05:11:46 +00:00
|
|
|
c.stack = c.stack[:0]
|
2014-04-07 22:24:51 +00:00
|
|
|
c.search(seek, c.bucket.root)
|
2014-03-01 16:13:59 +00:00
|
|
|
ref := &c.stack[len(c.stack)-1]
|
2014-01-31 18:18:51 +00:00
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// If the cursor is pointing to the end of page/node then return nil.
|
|
|
|
if ref.index >= ref.count() {
|
2014-04-07 22:24:51 +00:00
|
|
|
return nil, nil, 0
|
2014-01-31 18:18:51 +00:00
|
|
|
}
|
|
|
|
|
2014-04-07 22:24:51 +00:00
|
|
|
// If this is a bucket then return a nil value.
|
2014-03-01 16:13:59 +00:00
|
|
|
return c.keyValue()
|
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() {
|
|
|
|
for {
|
2014-02-20 18:55:04 +00:00
|
|
|
// Exit when we hit a leaf page.
|
2014-05-07 16:37:50 +00:00
|
|
|
var ref = &c.stack[len(c.stack)-1]
|
2014-03-01 16:13:59 +00:00
|
|
|
if ref.isLeaf() {
|
2014-02-11 15:41:22 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep adding pages pointing to the first element to the stack.
|
2014-03-01 16:13:59 +00:00
|
|
|
var pgid pgid
|
|
|
|
if ref.node != nil {
|
|
|
|
pgid = ref.node.inodes[ref.index].pgid
|
|
|
|
} else {
|
|
|
|
pgid = ref.page.branchPageElement(uint16(ref.index)).pgid
|
|
|
|
}
|
2014-04-07 22:24:51 +00:00
|
|
|
p, n := c.bucket.pageNode(pgid)
|
2014-03-01 16:13:59 +00:00
|
|
|
c.stack = append(c.stack, elemRef{page: p, node: n, index: 0})
|
2014-02-11 15:41:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-20 20:53:40 +00:00
|
|
|
// last moves the cursor to the last leaf element under the last page in the stack.
|
|
|
|
func (c *Cursor) last() {
|
|
|
|
for {
|
|
|
|
// Exit when we hit a leaf page.
|
2014-03-01 16:13:59 +00:00
|
|
|
ref := &c.stack[len(c.stack)-1]
|
|
|
|
if ref.isLeaf() {
|
2014-02-20 20:53:40 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep adding pages pointing to the last element in the stack.
|
2014-03-01 16:13:59 +00:00
|
|
|
var pgid pgid
|
|
|
|
if ref.node != nil {
|
|
|
|
pgid = ref.node.inodes[ref.index].pgid
|
|
|
|
} else {
|
|
|
|
pgid = ref.page.branchPageElement(uint16(ref.index)).pgid
|
|
|
|
}
|
2014-04-07 22:24:51 +00:00
|
|
|
p, n := c.bucket.pageNode(pgid)
|
2014-03-01 16:13:59 +00:00
|
|
|
|
|
|
|
var nextRef = elemRef{page: p, node: n}
|
|
|
|
nextRef.index = nextRef.count() - 1
|
|
|
|
c.stack = append(c.stack, nextRef)
|
2014-02-20 20:53:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-09 17:07:25 +00:00
|
|
|
// next moves to the next leaf element and returns the key and value.
|
|
|
|
// If the cursor is at the last leaf element then it stays there and returns nil.
|
|
|
|
func (c *Cursor) next() (key []byte, value []byte, flags uint32) {
|
|
|
|
// 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.
|
|
|
|
var i int
|
|
|
|
for i = len(c.stack) - 1; i >= 0; i-- {
|
|
|
|
elem := &c.stack[i]
|
|
|
|
if elem.index < elem.count()-1 {
|
|
|
|
elem.index++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've hit the root page then stop and return. This will leave the
|
|
|
|
// cursor on the last element of the last page.
|
|
|
|
if i == -1 {
|
|
|
|
return nil, nil, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise start from where we left off in the stack and find the
|
|
|
|
// first element of the first leaf page.
|
|
|
|
c.stack = c.stack[:i+1]
|
|
|
|
c.first()
|
|
|
|
return c.keyValue()
|
|
|
|
}
|
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// search recursively performs a binary search against a given page/node until it finds a given key.
|
|
|
|
func (c *Cursor) search(key []byte, pgid pgid) {
|
2014-04-07 22:24:51 +00:00
|
|
|
p, n := c.bucket.pageNode(pgid)
|
2014-03-01 16:13:59 +00:00
|
|
|
if p != nil {
|
2014-05-19 18:08:33 +00:00
|
|
|
_assert((p.flags&(branchPageFlag|leafPageFlag)) != 0, "invalid page type: %d: %x", p.id, p.flags)
|
2014-03-01 16:13:59 +00:00
|
|
|
}
|
|
|
|
e := elemRef{page: p, node: n}
|
2014-01-30 05:11:46 +00:00
|
|
|
c.stack = append(c.stack, e)
|
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// If we're on a leaf page/node then find the specific node.
|
|
|
|
if e.isLeaf() {
|
|
|
|
c.nsearch(key)
|
2014-01-30 05:11:46 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
if n != nil {
|
|
|
|
c.searchNode(key, n)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.searchPage(key, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cursor) searchNode(key []byte, n *node) {
|
|
|
|
var exact bool
|
|
|
|
index := sort.Search(len(n.inodes), 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(n.inodes[i].key, key)
|
|
|
|
if ret == 0 {
|
|
|
|
exact = true
|
|
|
|
}
|
|
|
|
return ret != -1
|
|
|
|
})
|
|
|
|
if !exact && index > 0 {
|
|
|
|
index--
|
|
|
|
}
|
|
|
|
c.stack[len(c.stack)-1].index = index
|
|
|
|
|
|
|
|
// Recursively search to the next page.
|
|
|
|
c.search(key, n.inodes[index].pgid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cursor) searchPage(key []byte, p *page) {
|
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-03-01 16:13:59 +00:00
|
|
|
c.stack[len(c.stack)-1].index = index
|
2014-01-30 05:11:46 +00:00
|
|
|
|
|
|
|
// Recursively search to the next page.
|
2014-03-01 16:13:59 +00:00
|
|
|
c.search(key, inodes[index].pgid)
|
2014-01-30 05:11:46 +00:00
|
|
|
}
|
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// nsearch searches the leaf node on the top of the stack for a key.
|
|
|
|
func (c *Cursor) nsearch(key []byte) {
|
2014-01-30 05:11:46 +00:00
|
|
|
e := &c.stack[len(c.stack)-1]
|
2014-03-01 16:13:59 +00:00
|
|
|
p, n := e.page, e.node
|
|
|
|
|
|
|
|
// If we have a node then search its inodes.
|
|
|
|
if n != nil {
|
|
|
|
index := sort.Search(len(n.inodes), func(i int) bool {
|
|
|
|
return bytes.Compare(n.inodes[i].key, key) != -1
|
|
|
|
})
|
|
|
|
e.index = index
|
|
|
|
return
|
|
|
|
}
|
2014-01-30 05:11:46 +00:00
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// If we have a page then search its leaf elements.
|
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-03-01 16:13:59 +00:00
|
|
|
e.index = index
|
2014-02-07 22:03:29 +00:00
|
|
|
}
|
|
|
|
|
2014-02-11 15:41:22 +00:00
|
|
|
// keyValue returns the key and value of the current leaf element.
|
2014-04-07 22:24:51 +00:00
|
|
|
func (c *Cursor) keyValue() ([]byte, []byte, uint32) {
|
2014-02-11 15:41:22 +00:00
|
|
|
ref := &c.stack[len(c.stack)-1]
|
2014-03-13 20:39:28 +00:00
|
|
|
if ref.count() == 0 || ref.index >= ref.count() {
|
2014-04-07 22:24:51 +00:00
|
|
|
return nil, nil, 0
|
2014-02-11 15:41:22 +00:00
|
|
|
}
|
2014-03-01 16:13:59 +00:00
|
|
|
|
|
|
|
// Retrieve value from node.
|
|
|
|
if ref.node != nil {
|
|
|
|
inode := &ref.node.inodes[ref.index]
|
2014-04-07 22:24:51 +00:00
|
|
|
return inode.key, inode.value, inode.flags
|
2014-03-01 16:13:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Or retrieve value from page.
|
|
|
|
elem := ref.page.leafPageElement(uint16(ref.index))
|
2014-04-07 22:24:51 +00:00
|
|
|
return elem.key(), elem.value(), elem.flags
|
2014-02-11 15:41:22 +00:00
|
|
|
}
|
|
|
|
|
2014-02-07 22:03:29 +00:00
|
|
|
// node returns the node that the cursor is currently positioned on.
|
2014-04-07 22:24:51 +00:00
|
|
|
func (c *Cursor) node() *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
|
|
|
|
2014-03-01 16:13:59 +00:00
|
|
|
// If the top of the stack is a leaf node then just return it.
|
|
|
|
if ref := &c.stack[len(c.stack)-1]; ref.node != nil && ref.isLeaf() {
|
|
|
|
return ref.node
|
|
|
|
}
|
|
|
|
|
2014-02-07 22:03:29 +00:00
|
|
|
// Start from root and traverse down the hierarchy.
|
2014-03-01 16:13:59 +00:00
|
|
|
var n = c.stack[0].node
|
|
|
|
if n == nil {
|
2014-04-07 22:24:51 +00:00
|
|
|
n = c.bucket.node(c.stack[0].page.id, nil)
|
2014-03-01 16:13:59 +00:00
|
|
|
}
|
2014-02-07 22:03:29 +00:00
|
|
|
for _, ref := range c.stack[:len(c.stack)-1] {
|
2014-02-08 05:55:25 +00:00
|
|
|
_assert(!n.isLeaf, "expected branch node")
|
|
|
|
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")
|
2014-02-07 22:03:29 +00:00
|
|
|
return n
|
2014-01-09 16:07:10 +00:00
|
|
|
}
|
2014-03-01 16:13:59 +00:00
|
|
|
|
|
|
|
// elemRef represents a reference to an element on a given page/node.
|
|
|
|
type elemRef struct {
|
|
|
|
page *page
|
|
|
|
node *node
|
|
|
|
index int
|
|
|
|
}
|
|
|
|
|
|
|
|
// isLeaf returns whether the ref is pointing at a leaf page/node.
|
|
|
|
func (r *elemRef) isLeaf() bool {
|
|
|
|
if r.node != nil {
|
|
|
|
return r.node.isLeaf
|
|
|
|
}
|
|
|
|
return (r.page.flags & leafPageFlag) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// count returns the number of inodes or page elements.
|
|
|
|
func (r *elemRef) count() int {
|
|
|
|
if r.node != nil {
|
|
|
|
return len(r.node.inodes)
|
|
|
|
}
|
|
|
|
return int(r.page.count)
|
|
|
|
}
|