diff options
author | Ben Johnson <benbjohnson@yahoo.com> | 2014-02-01 09:42:11 -0800 |
---|---|---|
committer | Ben Johnson <benbjohnson@yahoo.com> | 2014-02-01 09:42:11 -0800 |
commit | b2a78a23644bf7cddb81059bd19176ebbe44710e (patch) | |
tree | d6aa197560053444d76dce1bd198a584c051c7b9 /node.go | |
parent | Merge pull request #4 from benbjohnson/api (diff) | |
parent | Add RWTransaction.Put(). (diff) | |
download | dedo-b2a78a23644bf7cddb81059bd19176ebbe44710e.tar.gz dedo-b2a78a23644bf7cddb81059bd19176ebbe44710e.tar.xz |
Merge pull request #5 from benbjohnson/put
Add RWTransaction.Put().
Diffstat (limited to 'node.go')
-rw-r--r-- | node.go | 178 |
1 files changed, 178 insertions, 0 deletions
@@ -0,0 +1,178 @@ +package bolt + +import ( + "bytes" + "sort" + "unsafe" +) + +// node represents an in-memory, deserialized page. +type node struct { + isLeaf bool + key []byte + depth int + pgid pgid + parent *node + inodes inodes +} + +// size returns the size of the node after serialization. +func (n *node) size() int { + var elementSize int = n.pageElementSize() + + var size int = pageHeaderSize + for _, item := range n.inodes { + size += elementSize + len(item.key) + len(item.value) + } + return size +} + +// pageElementSize returns the size of each page element based on the type of node. +func (n *node) pageElementSize() int { + if n.isLeaf { + return leafPageElementSize + } + return branchPageElementSize +} + +// root returns the root node in the tree. +func (n *node) root() *node { + if n.parent == nil { + return n + } + return n.parent +} + +// put inserts a key/value. +func (n *node) put(oldKey, newKey, value []byte, pgid pgid) { + // Find insertion index. + index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, oldKey) != -1 }) + + // Add capacity and shift nodes if we don't have an exact match and need to insert. + exact := (len(n.inodes) > 0 && index < len(n.inodes) && bytes.Equal(n.inodes[index].key, oldKey)) + if !exact { + n.inodes = append(n.inodes, inode{}) + copy(n.inodes[index+1:], n.inodes[index:]) + } + + inode := &n.inodes[index] + inode.key = newKey + inode.value = value + inode.pgid = pgid +} + +// read initializes the node from a page. +func (n *node) read(p *page) { + n.pgid = p.id + n.isLeaf = ((p.flags & p_leaf) != 0) + n.inodes = make(inodes, int(p.count)) + + for i := 0; i < int(p.count); i++ { + inode := &n.inodes[i] + if n.isLeaf { + elem := p.leafPageElement(uint16(i)) + inode.key = elem.key() + inode.value = elem.value() + } else { + elem := p.branchPageElement(uint16(i)) + inode.pgid = elem.pgid + inode.key = elem.key() + } + } + + // Save first key so we can find the node in the parent when we spill. + if len(n.inodes) > 0 { + n.key = n.inodes[0].key + } else { + n.key = nil + } +} + +// write writes the items onto one or more pages. +func (n *node) write(p *page) { + // Initialize page. + if n.isLeaf { + p.flags |= p_leaf + } else { + p.flags |= p_branch + } + p.count = uint16(len(n.inodes)) + + // Loop over each item and write it to the page. + b := (*[maxAllocSize]byte)(unsafe.Pointer(&p.ptr))[n.pageElementSize()*len(n.inodes):] + for i, item := range n.inodes { + // Write the page element. + if n.isLeaf { + elem := p.leafPageElement(uint16(i)) + elem.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem))) + elem.ksize = uint32(len(item.key)) + elem.vsize = uint32(len(item.value)) + } else { + elem := p.branchPageElement(uint16(i)) + elem.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem))) + elem.ksize = uint32(len(item.key)) + elem.pgid = item.pgid + } + + // Write data for the element to the end of the page. + copy(b[0:], item.key) + b = b[len(item.key):] + copy(b[0:], item.value) + b = b[len(item.value):] + } +} + +// split divides up the node into appropriately sized nodes. +func (n *node) split(pageSize int) []*node { + // Ignore the split if the page doesn't have at least enough nodes for + // multiple pages or if the data can fit on a single page. + if len(n.inodes) <= (minKeysPerPage*2) || n.size() < pageSize { + return []*node{n} + } + + // Set fill threshold to 50%. + threshold := pageSize / 2 + + // Group into smaller pages and target a given fill size. + size := 0 + current := &node{isLeaf: n.isLeaf} + nodes := make([]*node, 0) + + for i, inode := range n.inodes { + elemSize := n.pageElementSize() + len(inode.key) + len(inode.value) + + if len(current.inodes) >= minKeysPerPage && i < len(n.inodes)-minKeysPerPage && size+elemSize > threshold { + size = pageHeaderSize + nodes = append(nodes, current) + current = &node{isLeaf: n.isLeaf} + } + + size += elemSize + current.inodes = append(current.inodes, inode) + } + nodes = append(nodes, current) + + return nodes +} + +// nodesByDepth sorts a list of branches by deepest first. +type nodesByDepth []*node + +func (s nodesByDepth) Len() int { return len(s) } +func (s nodesByDepth) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s nodesByDepth) Less(i, j int) bool { return s[i].depth > s[j].depth } + +// inode represents an internal node inside of a node. +// It can be used to point to elements in a page or point +// to an element which hasn't been added to a page yet. +type inode struct { + pgid pgid + key []byte + value []byte +} + +type inodes []inode + +func (s inodes) Len() int { return len(s) } +func (s inodes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s inodes) Less(i, j int) bool { return bytes.Compare(s[i].key, s[j].key) == -1 } |