| package bbolt |
| |
| import ( |
| "bytes" |
| "fmt" |
| "sort" |
| ) |
| |
| // Cursor represents an iterator that can traverse over all key/value pairs in a bucket in sorted order. |
| // Cursors see nested buckets with value == nil. |
| // Cursors can be obtained from a transaction and are valid as long as the transaction is open. |
| // |
| // Keys and values returned from the cursor are only valid for the life of the transaction. |
| // |
| // Changing data while traversing with a cursor may cause it to be invalidated |
| // and return unexpected keys and/or values. You must reposition your cursor |
| // after mutating data. |
| type Cursor struct { |
| bucket *Bucket |
| stack []elemRef |
| } |
| |
| // Bucket returns the bucket that this cursor was created from. |
| func (c *Cursor) Bucket() *Bucket { |
| return c.bucket |
| } |
| |
| // 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 and value are returned. |
| // The returned key and value are only valid for the life of the transaction. |
| func (c *Cursor) First() (key []byte, value []byte) { |
| _assert(c.bucket.tx.db != nil, "tx closed") |
| c.stack = c.stack[:0] |
| p, n := c.bucket.pageNode(c.bucket.root) |
| c.stack = append(c.stack, elemRef{page: p, node: n, index: 0}) |
| c.first() |
| |
| // If we land on an empty page then move to the next value. |
| // https://github.com/boltdb/bolt/issues/450 |
| if c.stack[len(c.stack)-1].count() == 0 { |
| c.next() |
| } |
| |
| k, v, flags := c.keyValue() |
| if (flags & uint32(bucketLeafFlag)) != 0 { |
| return k, nil |
| } |
| return k, v |
| |
| } |
| |
| // 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. |
| // The returned key and value are only valid for the life of the transaction. |
| func (c *Cursor) Last() (key []byte, value []byte) { |
| _assert(c.bucket.tx.db != nil, "tx closed") |
| c.stack = c.stack[:0] |
| p, n := c.bucket.pageNode(c.bucket.root) |
| ref := elemRef{page: p, node: n} |
| ref.index = ref.count() - 1 |
| c.stack = append(c.stack, ref) |
| c.last() |
| k, v, flags := c.keyValue() |
| if (flags & uint32(bucketLeafFlag)) != 0 { |
| return k, nil |
| } |
| return k, v |
| } |
| |
| // 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 and value are returned. |
| // The returned key and value are only valid for the life of the transaction. |
| func (c *Cursor) Next() (key []byte, value []byte) { |
| _assert(c.bucket.tx.db != nil, "tx closed") |
| k, v, flags := c.next() |
| if (flags & uint32(bucketLeafFlag)) != 0 { |
| return k, nil |
| } |
| return k, v |
| } |
| |
| // 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. |
| // The returned key and value are only valid for the life of the transaction. |
| func (c *Cursor) Prev() (key []byte, value []byte) { |
| _assert(c.bucket.tx.db != nil, "tx closed") |
| |
| // 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() |
| k, v, flags := c.keyValue() |
| if (flags & uint32(bucketLeafFlag)) != 0 { |
| return k, nil |
| } |
| return k, v |
| } |
| |
| // 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, a nil key is returned. |
| // The returned key and value are only valid for the life of the transaction. |
| func (c *Cursor) Seek(seek []byte) (key []byte, value []byte) { |
| k, v, flags := c.seek(seek) |
| |
| // 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() |
| } |
| |
| if k == nil { |
| return nil, nil |
| } else if (flags & uint32(bucketLeafFlag)) != 0 { |
| return k, nil |
| } |
| return k, v |
| } |
| |
| // Delete removes the current key/value under the cursor from the bucket. |
| // Delete fails if current key/value is a bucket or if the transaction is not writable. |
| func (c *Cursor) Delete() error { |
| if c.bucket.tx.db == nil { |
| return ErrTxClosed |
| } else if !c.bucket.Writable() { |
| return ErrTxNotWritable |
| } |
| |
| key, _, flags := c.keyValue() |
| // Return an error if current value is a bucket. |
| if (flags & bucketLeafFlag) != 0 { |
| return ErrIncompatibleValue |
| } |
| c.node().del(key) |
| |
| return nil |
| } |
| |
| // seek moves the cursor to a given key and returns it. |
| // If the key does not exist then the next key is used. |
| func (c *Cursor) seek(seek []byte) (key []byte, value []byte, flags uint32) { |
| _assert(c.bucket.tx.db != nil, "tx closed") |
| |
| // Start from root page/node and traverse to correct page. |
| c.stack = c.stack[:0] |
| c.search(seek, c.bucket.root) |
| |
| // If this is a bucket then return a nil value. |
| return c.keyValue() |
| } |
| |
| // first moves the cursor to the first leaf element under the last page in the stack. |
| func (c *Cursor) first() { |
| for { |
| // Exit when we hit a leaf page. |
| var ref = &c.stack[len(c.stack)-1] |
| if ref.isLeaf() { |
| break |
| } |
| |
| // Keep adding pages pointing to the first element to the stack. |
| var pgid pgid |
| if ref.node != nil { |
| pgid = ref.node.inodes[ref.index].pgid |
| } else { |
| pgid = ref.page.branchPageElement(uint16(ref.index)).pgid |
| } |
| p, n := c.bucket.pageNode(pgid) |
| c.stack = append(c.stack, elemRef{page: p, node: n, index: 0}) |
| } |
| } |
| |
| // 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. |
| ref := &c.stack[len(c.stack)-1] |
| if ref.isLeaf() { |
| break |
| } |
| |
| // Keep adding pages pointing to the last element in the stack. |
| var pgid pgid |
| if ref.node != nil { |
| pgid = ref.node.inodes[ref.index].pgid |
| } else { |
| pgid = ref.page.branchPageElement(uint16(ref.index)).pgid |
| } |
| p, n := c.bucket.pageNode(pgid) |
| |
| var nextRef = elemRef{page: p, node: n} |
| nextRef.index = nextRef.count() - 1 |
| c.stack = append(c.stack, nextRef) |
| } |
| } |
| |
| // 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) { |
| for { |
| // 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() |
| |
| // If this is an empty page then restart and move back up the stack. |
| // https://github.com/boltdb/bolt/issues/450 |
| if c.stack[len(c.stack)-1].count() == 0 { |
| continue |
| } |
| |
| return c.keyValue() |
| } |
| } |
| |
| // 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) { |
| p, n := c.bucket.pageNode(pgid) |
| if p != nil && (p.flags&(branchPageFlag|leafPageFlag)) == 0 { |
| panic(fmt.Sprintf("invalid page type: %d: %x", p.id, p.flags)) |
| } |
| e := elemRef{page: p, node: n} |
| c.stack = append(c.stack, e) |
| |
| // If we're on a leaf page/node then find the specific node. |
| if e.isLeaf() { |
| c.nsearch(key) |
| return |
| } |
| |
| 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) { |
| // 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-- |
| } |
| c.stack[len(c.stack)-1].index = index |
| |
| // Recursively search to the next page. |
| c.search(key, inodes[index].pgid) |
| } |
| |
| // nsearch searches the leaf node on the top of the stack for a key. |
| func (c *Cursor) nsearch(key []byte) { |
| e := &c.stack[len(c.stack)-1] |
| 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 |
| } |
| |
| // If we have a page then search its leaf elements. |
| inodes := p.leafPageElements() |
| index := sort.Search(int(p.count), func(i int) bool { |
| return bytes.Compare(inodes[i].key(), key) != -1 |
| }) |
| e.index = index |
| } |
| |
| // keyValue returns the key and value of the current leaf element. |
| func (c *Cursor) keyValue() ([]byte, []byte, uint32) { |
| ref := &c.stack[len(c.stack)-1] |
| |
| // If the cursor is pointing to the end of page/node then return nil. |
| if ref.count() == 0 || ref.index >= ref.count() { |
| return nil, nil, 0 |
| } |
| |
| // Retrieve value from node. |
| if ref.node != nil { |
| inode := &ref.node.inodes[ref.index] |
| return inode.key, inode.value, inode.flags |
| } |
| |
| // Or retrieve value from page. |
| elem := ref.page.leafPageElement(uint16(ref.index)) |
| return elem.key(), elem.value(), elem.flags |
| } |
| |
| // node returns the node that the cursor is currently positioned on. |
| func (c *Cursor) node() *node { |
| _assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack") |
| |
| // 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 |
| } |
| |
| // Start from root and traverse down the hierarchy. |
| var n = c.stack[0].node |
| if n == nil { |
| n = c.bucket.node(c.stack[0].page.id, nil) |
| } |
| for _, ref := range c.stack[:len(c.stack)-1] { |
| _assert(!n.isLeaf, "expected branch node") |
| n = n.childAt(int(ref.index)) |
| } |
| _assert(n.isLeaf, "expected leaf node") |
| return n |
| } |
| |
| // 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) |
| } |