package bolt import ( "bytes" "sort" "unsafe" ) // leaf represents an in-memory, deserialized leaf page. type leaf struct { pgid pgid parent *branch items leafItems } // size returns the size of the leaf after serialization. func (l *leaf) size() int { var size int = pageHeaderSize for _, item := range l.items { size += lnodeSize + len(item.key) + len(item.value) } return size } // put inserts or replaces a key on a leaf page. func (l *leaf) put(key []byte, value []byte) { // Find insertion index. index := sort.Search(len(l.items), func(i int) bool { return bytes.Compare(l.items[i].key, key) != -1 }) // If there is no existing key then add a new item. if index == len(l.items) { l.items = append(l.items, leafItem{}) } else if len(l.items) == 0 || !bytes.Equal(l.items[index].key, key) { l.items = append(l.items, leafItem{}) copy(l.items[index+1:], l.items[index:]) } l.items[index].key = key l.items[index].value = value } // read initializes the item data from an on-disk page. func (l *leaf) read(p *page) { l.pgid = p.id l.items = make(leafItems, int(p.count)) lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&p.ptr)) for i := 0; i < int(p.count); i++ { lnode := &lnodes[i] item := &l.items[i] item.key = lnode.key() item.value = lnode.value() } } // write writes the items onto one or more leaf pages. func (l *leaf) write(p *page) { // Initialize page. p.flags |= p_leaf p.count = uint16(len(l.items)) // Loop over each item and write it to the page. lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&p.ptr)) b := (*[maxAllocSize]byte)(unsafe.Pointer(&p.ptr))[lnodeSize*len(l.items):] for index, item := range l.items { // Write node. lnode := &lnodes[index] lnode.pos = uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(lnode))) lnode.ksize = uint32(len(item.key)) lnode.vsize = uint32(len(item.value)) // Write data 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 noes in the page into appropriately sized groups. func (l *leaf) split(pageSize int) []*leaf { // 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(l.items) <= (minKeysPerPage*2) || l.size() < pageSize { return []*leaf{l} } // Set fill threshold to 50%. threshold := pageSize / 2 // Otherwise group into smaller pages and target a given fill size. size := 0 current := &leaf{} leafs := make([]*leaf, 0) for index, item := range l.items { nodeSize := lnodeSize + len(item.key) + len(item.value) if len(current.items) >= minKeysPerPage && index < len(l.items)-minKeysPerPage && size+nodeSize > threshold { size = pageHeaderSize leafs = append(leafs, current) current = &leaf{} } size += nodeSize current.items = append(current.items, item) } leafs = append(leafs, current) return leafs } type leafItems []leafItem type leafItem struct { key []byte value []byte } func (s leafItems) Len() int { return len(s) } func (s leafItems) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s leafItems) Less(i, j int) bool { return bytes.Compare(s[i].key, s[j].key) == -1 }