diff options
author | Ben Johnson <benbjohnson@yahoo.com> | 2014-01-28 15:16:22 -0500 |
---|---|---|
committer | Ben Johnson <benbjohnson@yahoo.com> | 2014-01-28 15:16:22 -0500 |
commit | a942c1d1686f2af42a8a2f3d59d7e1b5afd0922a (patch) | |
tree | deb42e921e50c12ec9811ce6578ec2bfea7de729 /tpage.go | |
parent | Clean up test suite. (diff) | |
download | dedo-a942c1d1686f2af42a8a2f3d59d7e1b5afd0922a.tar.gz dedo-a942c1d1686f2af42a8a2f3d59d7e1b5afd0922a.tar.xz |
Add tpage.put() test.
Diffstat (limited to 'tpage.go')
-rw-r--r-- | tpage.go | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/tpage.go b/tpage.go new file mode 100644 index 0000000..c4dc83f --- /dev/null +++ b/tpage.go @@ -0,0 +1,127 @@ +package bolt + +import ( + "bytes" + "sort" + "unsafe" +) + +// tpage represents a temporary, in-memory leaf page. +// It is deserialized from an memory-mapped page and is not restricted by page size. +type tpage struct { + nodes tnodes +} + +// allocator is a function that returns a set of contiguous pages. +type allocator func(count int) (*page, error) + +// put inserts or replaces a key on a leaf page. +func (p *tpage) put(key []byte, value []byte) { + // Find insertion index. + index := sort.Search(len(p.nodes), func(i int) bool { return bytes.Compare(p.nodes[i].key, key) != -1 }) + + // If there is no existing key then add a new node. + if index == len(p.nodes) { + p.nodes = append(p.nodes, tnode{}) + } else if len(p.nodes) == 0 || !bytes.Equal(p.nodes[index].key, key) { + p.nodes = append(p.nodes, tnode{}) + copy(p.nodes[index+1:], p.nodes[index:]) + } + p.nodes[index].key = key + p.nodes[index].value = value +} + +// read initializes the node data from an on-disk page. +func (p *tpage) read(page *page) { + p.nodes = make(tnodes, page.count) + lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&page.ptr)) + for i := 0; i < int(page.count); i++ { + lnode := lnodes[i] + n := &p.nodes[i] + n.key = lnode.key() + n.value = lnode.value() + } +} + +// write writes the nodes onto one or more leaf pages. +func (p *tpage) write(pageSize int, allocate allocator) ([]*page, error) { + var pages []*page + + for _, nodes := range p.split(pageSize) { + // Determine the total page size. + var size int = pageHeaderSize + for _, node := range p.nodes { + size += lnodeSize + len(node.key) + len(node.value) + } + + // Allocate pages. + page, err := allocate(size) + if err != nil { + return nil, err + } + page.flags |= p_leaf + page.count = uint16(len(nodes)) + + // Loop over each node and write it to the page. + lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&page.ptr)) + b := (*[maxPageAllocSize]byte)(unsafe.Pointer(&page.ptr))[lnodeSize*len(nodes):] + for index, node := range nodes { + // Write node. + lnode := &lnodes[index] + lnode.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(&lnode))) + lnode.ksize = uint32(len(node.key)) + lnode.vsize = uint32(len(node.value)) + + // Write data to the end of the node. + copy(b[:], node.key) + b = b[len(node.key):] + copy(b[:], node.value) + b = b[len(node.value):] + } + + pages = append(pages, page) + } + + return pages, nil +} + +// split divides up the noes in the page into appropriately sized groups. +func (p *tpage) split(pageSize int) []tnodes { + // If we only have enough nodes for one page then just return the nodes. + if len(p.nodes) <= minKeysPerPage { + return []tnodes{p.nodes} + } + + // If we're not larger than one page then just return the nodes. + var totalSize int = pageHeaderSize + for _, node := range p.nodes { + totalSize += lnodeSize + len(node.key) + len(node.value) + } + if totalSize < pageSize { + return []tnodes{p.nodes} + } + + // Otherwise group into smaller pages and target a given fill size. + var size int + var group tnodes + var groups []tnodes + + // Set fill threshold to 25%. + threshold := pageSize >> 4 + + for _, node := range p.nodes { + nodeSize := lnodeSize + len(node.key) + len(node.value) + + // TODO(benbjohnson): Don't create a new group for just the last node. + if group == nil || (len(group) > minKeysPerPage && size+nodeSize > threshold) { + size = pageHeaderSize + group = make(tnodes, 0) + groups = append(groups, group) + } + + size += nodeSize + group = append(group, node) + } + + return groups +} |