package bbolt
import (
)
type node struct {
bucket *Bucket
isLeaf bool
unbalanced bool
spilled bool
key []byte
pgid pgid
parent *node
children nodes
inodes inodes
}
func ( *node) () *node {
if .parent == nil {
return
}
return .parent.()
}
func ( *node) () int {
if .isLeaf {
return 1
}
return 2
}
func ( *node) () int {
, := pageHeaderSize, .pageElementSize()
for := 0; < len(.inodes); ++ {
:= &.inodes[]
+= + uintptr(len(.key)) + uintptr(len(.value))
}
return int()
}
func ( *node) ( uintptr) bool {
, := pageHeaderSize, .pageElementSize()
for := 0; < len(.inodes); ++ {
:= &.inodes[]
+= + uintptr(len(.key)) + uintptr(len(.value))
if >= {
return false
}
}
return true
}
func ( *node) () uintptr {
if .isLeaf {
return leafPageElementSize
}
return branchPageElementSize
}
func ( *node) ( int) *node {
if .isLeaf {
panic(fmt.Sprintf("invalid childAt(%d) on a leaf node", ))
}
return .bucket.node(.inodes[].pgid, )
}
func ( *node) ( *node) int {
:= sort.Search(len(.inodes), func( int) bool { return bytes.Compare(.inodes[].key, .key) != -1 })
return
}
func ( *node) () int {
return len(.inodes)
}
func ( *node) () *node {
if .parent == nil {
return nil
}
:= .parent.childIndex()
if >= .parent.numChildren()-1 {
return nil
}
return .parent.childAt( + 1)
}
func ( *node) () *node {
if .parent == nil {
return nil
}
:= .parent.childIndex()
if == 0 {
return nil
}
return .parent.childAt( - 1)
}
func ( *node) (, , []byte, pgid, uint32) {
if >= .bucket.tx.meta.pgid {
panic(fmt.Sprintf("pgid (%d) above high water mark (%d)", , .bucket.tx.meta.pgid))
} else if len() <= 0 {
panic("put: zero-length old key")
} else if len() <= 0 {
panic("put: zero-length new key")
}
:= sort.Search(len(.inodes), func( int) bool { return bytes.Compare(.inodes[].key, ) != -1 })
:= (len(.inodes) > 0 && < len(.inodes) && bytes.Equal(.inodes[].key, ))
if ! {
.inodes = append(.inodes, inode{})
copy(.inodes[+1:], .inodes[:])
}
:= &.inodes[]
.flags =
.key =
.value =
.pgid =
_assert(len(.key) > 0, "put: zero-length inode key")
}
func ( *node) ( []byte) {
:= sort.Search(len(.inodes), func( int) bool { return bytes.Compare(.inodes[].key, ) != -1 })
if >= len(.inodes) || !bytes.Equal(.inodes[].key, ) {
return
}
.inodes = append(.inodes[:], .inodes[+1:]...)
.unbalanced = true
}
func ( *node) ( *page) {
.pgid = .id
.isLeaf = ((.flags & leafPageFlag) != 0)
.inodes = make(inodes, int(.count))
for := 0; < int(.count); ++ {
:= &.inodes[]
if .isLeaf {
:= .leafPageElement(uint16())
.flags = .flags
.key = .key()
.value = .value()
} else {
:= .branchPageElement(uint16())
.pgid = .pgid
.key = .key()
}
_assert(len(.key) > 0, "read: zero-length inode key")
}
if len(.inodes) > 0 {
.key = .inodes[0].key
_assert(len(.key) > 0, "read: zero-length node key")
} else {
.key = nil
}
}
func ( *node) ( *page) {
if .isLeaf {
.flags |= leafPageFlag
} else {
.flags |= branchPageFlag
}
if len(.inodes) >= 0xFFFF {
panic(fmt.Sprintf("inode overflow: %d (pgid=%d)", len(.inodes), .id))
}
.count = uint16(len(.inodes))
if .count == 0 {
return
}
:= unsafe.Sizeof(*) + .pageElementSize()*uintptr(len(.inodes))
for , := range .inodes {
_assert(len(.key) > 0, "write: zero-length inode key")
:= len(.key) + len(.value)
:= unsafeByteSlice(unsafe.Pointer(), , 0, )
+= uintptr()
if .isLeaf {
:= .leafPageElement(uint16())
.pos = uint32(uintptr(unsafe.Pointer(&[0])) - uintptr(unsafe.Pointer()))
.flags = .flags
.ksize = uint32(len(.key))
.vsize = uint32(len(.value))
} else {
:= .branchPageElement(uint16())
.pos = uint32(uintptr(unsafe.Pointer(&[0])) - uintptr(unsafe.Pointer()))
.ksize = uint32(len(.key))
.pgid = .pgid
_assert(.pgid != .id, "write: circular dependency occurred")
}
:= copy(, .key)
copy([:], .value)
}
}
func ( *node) ( uintptr) []*node {
var []*node
:=
for {
, := .splitTwo()
= append(, )
if == nil {
break
}
=
}
return
}
func ( *node) ( uintptr) (*node, *node) {
if len(.inodes) <= (minKeysPerPage*2) || .sizeLessThan() {
return , nil
}
var = .bucket.FillPercent
if < minFillPercent {
= minFillPercent
} else if > maxFillPercent {
= maxFillPercent
}
:= int(float64() * )
, := .splitIndex()
if .parent == nil {
.parent = &node{bucket: .bucket, children: []*node{}}
}
:= &node{bucket: .bucket, isLeaf: .isLeaf, parent: .parent}
.parent.children = append(.parent.children, )
.inodes = .inodes[:]
.inodes = .inodes[:]
.bucket.tx.stats.Split++
return ,
}
func ( *node) ( int) (, uintptr) {
= pageHeaderSize
for := 0; < len(.inodes)-minKeysPerPage; ++ {
= uintptr()
:= .inodes[]
:= .pageElementSize() + uintptr(len(.key)) + uintptr(len(.value))
if >= minKeysPerPage && + > uintptr() {
break
}
+=
}
return
}
func ( *node) () error {
var = .bucket.tx
if .spilled {
return nil
}
sort.Sort(.children)
for := 0; < len(.children); ++ {
if := .children[].(); != nil {
return
}
}
.children = nil
var = .split(uintptr(.db.pageSize))
for , := range {
if .pgid > 0 {
.db.freelist.free(.meta.txid, .page(.pgid))
.pgid = 0
}
, := .allocate((.size() + .db.pageSize - 1) / .db.pageSize)
if != nil {
return
}
if .id >= .meta.pgid {
panic(fmt.Sprintf("pgid (%d) above high water mark (%d)", .id, .meta.pgid))
}
.pgid = .id
.write()
.spilled = true
if .parent != nil {
var = .key
if == nil {
= .inodes[0].key
}
.parent.put(, .inodes[0].key, nil, .pgid, 0)
.key = .inodes[0].key
_assert(len(.key) > 0, "spill: zero-length node key")
}
.stats.Spill++
}
if .parent != nil && .parent.pgid == 0 {
.children = nil
return .parent.()
}
return nil
}
func ( *node) () {
if !.unbalanced {
return
}
.unbalanced = false
.bucket.tx.stats.Rebalance++
var = .bucket.tx.db.pageSize / 4
if .size() > && len(.inodes) > .minKeys() {
return
}
if .parent == nil {
if !.isLeaf && len(.inodes) == 1 {
:= .bucket.node(.inodes[0].pgid, )
.isLeaf = .isLeaf
.inodes = .inodes[:]
.children = .children
for , := range .inodes {
if , := .bucket.nodes[.pgid]; {
.parent =
}
}
.parent = nil
delete(.bucket.nodes, .pgid)
.free()
}
return
}
if .numChildren() == 0 {
.parent.del(.key)
.parent.removeChild()
delete(.bucket.nodes, .pgid)
.free()
.parent.()
return
}
_assert(.parent.numChildren() > 1, "parent must have at least 2 children")
var *node
var = (.parent.childIndex() == 0)
if {
= .nextSibling()
} else {
= .prevSibling()
}
if {
for , := range .inodes {
if , := .bucket.nodes[.pgid]; {
.parent.removeChild()
.parent =
.parent.children = append(.parent.children, )
}
}
.inodes = append(.inodes, .inodes...)
.parent.del(.key)
.parent.removeChild()
delete(.bucket.nodes, .pgid)
.free()
} else {
for , := range .inodes {
if , := .bucket.nodes[.pgid]; {
.parent.removeChild()
.parent =
.parent.children = append(.parent.children, )
}
}
.inodes = append(.inodes, .inodes...)
.parent.del(.key)
.parent.removeChild()
delete(.bucket.nodes, .pgid)
.free()
}
.parent.()
}
func ( *node) ( *node) {
for , := range .children {
if == {
.children = append(.children[:], .children[+1:]...)
return
}
}
}
func ( *node) () {
if .key != nil {
:= make([]byte, len(.key))
copy(, .key)
.key =
_assert(.pgid == 0 || len(.key) > 0, "dereference: zero-length node key on existing node")
}
for := range .inodes {
:= &.inodes[]
:= make([]byte, len(.key))
copy(, .key)
.key =
_assert(len(.key) > 0, "dereference: zero-length inode key")
:= make([]byte, len(.value))
copy(, .value)
.value =
}
for , := range .children {
.()
}
.bucket.tx.stats.NodeDeref++
}
func ( *node) () {
if .pgid != 0 {
.bucket.tx.db.freelist.free(.bucket.tx.meta.txid, .bucket.tx.page(.pgid))
.pgid = 0
}
}
type nodes []*node
func ( nodes) () int { return len() }
func ( nodes) (, int) { [], [] = [], [] }
func ( nodes) (, int) bool {
return bytes.Compare([].inodes[0].key, [].inodes[0].key) == -1
}
type inode struct {
flags uint32
pgid pgid
key []byte
value []byte
}
type inodes []inode