From 84939c21f68e6d4158cf2506851ab4db2f0afe78 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Fri, 7 Feb 2014 15:03:29 -0700 Subject: [PATCH] Refactor node lookup. --- assert.go | 12 ++++++++++++ bucket.go | 2 +- cursor.go | 49 ++++++++++++++++++++++++++++++------------------ node.go | 9 ++++++++- page.go | 6 ++++++ rwtransaction.go | 33 ++++++++++++++++---------------- 6 files changed, 74 insertions(+), 37 deletions(-) create mode 100644 assert.go diff --git a/assert.go b/assert.go new file mode 100644 index 0000000..912e484 --- /dev/null +++ b/assert.go @@ -0,0 +1,12 @@ +package bolt + +import "fmt" + +// TODO(benbjohnson): Remove assertions before release. + +// __assert__ will panic with a given formatted message if the given condition is false. +func __assert__(condition bool, msg string, v ...interface{}) { + if !condition { + panic(fmt.Sprintf("assertion failed: " + msg, v...)) + } +} diff --git a/bucket.go b/bucket.go index b1c1cc0..28c570e 100644 --- a/bucket.go +++ b/bucket.go @@ -20,6 +20,6 @@ func (b *Bucket) cursor() *Cursor { return &Cursor{ transaction: b.transaction, root: b.root, - stack: make([]elem, 0), + stack: make([]pageElementRef, 0), } } diff --git a/cursor.go b/cursor.go index 3df325a..2f65911 100644 --- a/cursor.go +++ b/cursor.go @@ -8,13 +8,7 @@ import ( type Cursor struct { transaction *Transaction root pgid - stack []elem -} - -// elem represents a node on a page that's on the cursor's stack. -type elem struct { - page *page - index uint16 + stack []pageElementRef } // First moves the cursor to the first item in the bucket and returns its key and data. @@ -41,18 +35,18 @@ func (c *Cursor) Get(key []byte) []byte { } // If our target node isn't the same key as what's passed in then return nil. - if !bytes.Equal(key, c.node().key()) { + if !bytes.Equal(key, c.element().key()) { return nil } - return c.node().value() + return c.element().value() } func (c *Cursor) search(key []byte, p *page) { if (p.flags & (p_branch | p_leaf)) == 0 { panic("invalid page type: " + p.typ()) } - e := elem{page: p} + e := pageElementRef{page: p} c.stack = append(c.stack, e) // If we're on a leaf page then find the specific node. @@ -77,10 +71,10 @@ func (c *Cursor) search(key []byte, p *page) { if !exact && index > 0 { index-- } - e.index = uint16(index) + c.stack[len(c.stack)-1].index = uint16(index) // Recursively search to the next page. - c.search(key, c.transaction.page(inodes[e.index].pgid)) + c.search(key, c.transaction.page(inodes[index].pgid)) } // nsearch searches a leaf node for the index of the node that matches key. @@ -97,8 +91,8 @@ func (c *Cursor) nsearch(key []byte, p *page) { // top returns the page and leaf node that the cursor is currently pointing at. func (c *Cursor) top() (*page, uint16) { - elem := c.stack[len(c.stack)-1] - return elem.page, elem.index + ptr := c.stack[len(c.stack)-1] + return ptr.page, ptr.index } // page returns the page that the cursor is currently pointing at. @@ -106,8 +100,27 @@ func (c *Cursor) page() *page { return c.stack[len(c.stack)-1].page } -// node returns the leaf node that the cursor is currently positioned on. -func (c *Cursor) node() *leafPageElement { - elem := c.stack[len(c.stack)-1] - return elem.page.leafPageElement(elem.index) +// 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) } + +// node returns the node that the cursor is currently positioned on. +func (c *Cursor) node(t *RWTransaction) *node { + if len(c.stack) == 0 { + return nil + } + + // 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] { + __assert__(!n.isLeaf, "expected branch node") + __assert__(ref.page.id == n.pgid, "node/page mismatch a: %d != %d", ref.page.id, n.childAt(ref.index).pgid) + n = n.childAt(ref.index) + } + __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) + return n +} + diff --git a/node.go b/node.go index 8229ae3..111f12c 100644 --- a/node.go +++ b/node.go @@ -8,6 +8,7 @@ import ( // node represents an in-memory, deserialized page. type node struct { + transaction *RWTransaction isLeaf bool key []byte depth int @@ -43,6 +44,12 @@ func (n *node) root() *node { return n.parent.root() } +// childAt returns the child node at a given index. +func (n *node) childAt(index uint16) *node { + __assert__(!n.isLeaf, "invalid childAt(%d) on a leaf node", index) + return n.transaction.node(n.inodes[index].pgid, n) +} + // put inserts a key/value. func (n *node) put(oldKey, newKey, value []byte, pgid pgid) { // Find insertion index. @@ -160,7 +167,7 @@ func (n *node) split(pageSize int) []*node { if len(current.inodes) >= minKeysPerPage && i < len(inodes)-minKeysPerPage && size+elemSize > threshold { size = pageHeaderSize nodes = append(nodes, current) - current = &node{isLeaf: n.isLeaf} + current = &node{transaction: n.transaction, isLeaf: n.isLeaf} } size += elemSize diff --git a/page.go b/page.go index 7c5a91c..f4dd19c 100644 --- a/page.go +++ b/page.go @@ -33,6 +33,12 @@ type page struct { ptr uintptr } +// pageElementRef represents a reference to an element on a given page. +type pageElementRef struct { + page *page + index uint16 +} + // typ returns a human readable page type string used for debugging. func (p *page) typ() string { if (p.flags & p_branch) != 0 { diff --git a/rwtransaction.go b/rwtransaction.go index fed59a6..ed44e3d 100644 --- a/rwtransaction.go +++ b/rwtransaction.go @@ -74,7 +74,7 @@ func (t *RWTransaction) Put(name string, key []byte, value []byte) error { c.Get(key) // Insert the key/value. - t.node(c.stack).put(key, key, value, 0) + c.node(t).put(key, key, value, 0) return nil } @@ -90,7 +90,7 @@ func (t *RWTransaction) Delete(name string, key []byte) error { c.Get(key) // Delete the node if we have a matching key. - t.node(c.stack).del(key) + c.node(t).del(key) return nil } @@ -186,7 +186,7 @@ func (t *RWTransaction) spill() { // If this is a root node that split then create a parent node. if n.parent == nil && len(newNodes) > 1 { - n.parent = &node{isLeaf: false} + n.parent = &node{transaction: t, isLeaf: false} nodes = append(nodes, n.parent) } @@ -261,25 +261,24 @@ func (t *RWTransaction) writeMeta() error { return nil } -// node retrieves a node based a cursor stack. -func (t *RWTransaction) node(stack []elem) *node { - if len(stack) == 0 { - return nil - } +// TODO(benbjohnson): Look up node by page id instead of by stack. Determine depth recursively by parent. +// TODO(benbjohnson): prevSibling() +// TODO(benbjohnson): nextSibling() - // Retrieve branch if it has already been fetched. - e := &stack[len(stack)-1] - id := e.page.id - if n := t.nodes[id]; n != nil { +// node creates a node from a page and associates it with a given parent. +func (t *RWTransaction) node(pgid pgid, parent *node) *node { + // Retrieve node if it has already been fetched. + if n := t.nodes[pgid]; n != nil { return n } // Otherwise create a branch and cache it. - n := &node{} - n.read(t.page(id)) - n.depth = len(stack) - 1 - n.parent = t.node(stack[:len(stack)-1]) - t.nodes[id] = n + n := &node{transaction: t, parent: parent} + if n.parent != nil { + n.depth = n.parent.depth + 1 + } + n.read(t.page(pgid)) + t.nodes[pgid] = n return n }