diff --git a/bucket.go b/bucket.go index 0950f77..b5a796d 100644 --- a/bucket.go +++ b/bucket.go @@ -523,7 +523,7 @@ func (b *Bucket) _forEachPageNode(pgId common.Pgid, depth int, fn func(*common.P } else { if !n.isLeaf { for _, inode := range n.inodes { - b._forEachPageNode(inode.pgid, depth+1, fn) + b._forEachPageNode(inode.Pgid(), depth+1, fn) } } } @@ -602,9 +602,9 @@ func (b *Bucket) inlineable() bool { // our threshold for inline bucket size. var size = common.PageHeaderSize for _, inode := range n.inodes { - size += common.LeafPageElementSize + uintptr(len(inode.key)) + uintptr(len(inode.value)) + size += common.LeafPageElementSize + uintptr(len(inode.Key())) + uintptr(len(inode.Value())) - if inode.flags&common.BucketLeafFlag != 0 { + if inode.Flags()&common.BucketLeafFlag != 0 { return false } else if size > b.maxInlineBucketSize() { return false diff --git a/cursor.go b/cursor.go index f08da54..209c960 100644 --- a/cursor.go +++ b/cursor.go @@ -176,7 +176,7 @@ func (c *Cursor) goToFirstElementOnTheStack() { // Keep adding pages pointing to the first element to the stack. var pgId common.Pgid if ref.node != nil { - pgId = ref.node.inodes[ref.index].pgid + pgId = ref.node.inodes[ref.index].Pgid() } else { pgId = ref.page.BranchPageElement(uint16(ref.index)).Pgid() } @@ -197,7 +197,7 @@ func (c *Cursor) last() { // Keep adding pages pointing to the last element in the stack. var pgId common.Pgid if ref.node != nil { - pgId = ref.node.inodes[ref.index].pgid + pgId = ref.node.inodes[ref.index].Pgid() } else { pgId = ref.page.BranchPageElement(uint16(ref.index)).Pgid() } @@ -296,7 +296,7 @@ func (c *Cursor) searchNode(key []byte, n *node) { index := sort.Search(len(n.inodes), func(i int) bool { // TODO(benbjohnson): Optimize this range search. It's a bit hacky right now. // sort.Search() finds the lowest index where f() != -1 but we need the highest index. - ret := bytes.Compare(n.inodes[i].key, key) + ret := bytes.Compare(n.inodes[i].Key(), key) if ret == 0 { exact = true } @@ -308,7 +308,7 @@ func (c *Cursor) searchNode(key []byte, n *node) { c.stack[len(c.stack)-1].index = index // Recursively search to the next page. - c.search(key, n.inodes[index].pgid) + c.search(key, n.inodes[index].Pgid()) } func (c *Cursor) searchPage(key []byte, p *common.Page) { @@ -342,7 +342,7 @@ func (c *Cursor) nsearch(key []byte) { // If we have a node then search its inodes. if n != nil { index := sort.Search(len(n.inodes), func(i int) bool { - return bytes.Compare(n.inodes[i].key, key) != -1 + return bytes.Compare(n.inodes[i].Key(), key) != -1 }) e.index = index return @@ -368,7 +368,7 @@ func (c *Cursor) keyValue() ([]byte, []byte, uint32) { // Retrieve value from node. if ref.node != nil { inode := &ref.node.inodes[ref.index] - return inode.key, inode.value, inode.flags + return inode.Key(), inode.Value(), inode.Flags() } // Or retrieve value from page. diff --git a/internal/common/inode.go b/internal/common/inode.go new file mode 100644 index 0000000..f8711cf --- /dev/null +++ b/internal/common/inode.go @@ -0,0 +1,45 @@ +package common + +// 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 { + flags uint32 + pgid Pgid + key []byte + value []byte +} + +type Inodes []Inode + +func (in *Inode) Flags() uint32 { + return in.flags +} + +func (in *Inode) SetFlags(flags uint32) { + in.flags = flags +} + +func (in *Inode) Pgid() Pgid { + return in.pgid +} + +func (in *Inode) SetPgid(id Pgid) { + in.pgid = id +} + +func (in *Inode) Key() []byte { + return in.key +} + +func (in *Inode) SetKey(key []byte) { + in.key = key +} + +func (in *Inode) Value() []byte { + return in.value +} + +func (in *Inode) SetValue(value []byte) { + in.value = value +} diff --git a/node.go b/node.go index 9769345..b97028d 100644 --- a/node.go +++ b/node.go @@ -19,7 +19,7 @@ type node struct { pgid common.Pgid parent *node children nodes - inodes inodes + inodes common.Inodes } // root returns the top-level node this node is attached to. @@ -43,7 +43,7 @@ func (n *node) size() int { sz, elsz := common.PageHeaderSize, n.pageElementSize() for i := 0; i < len(n.inodes); i++ { item := &n.inodes[i] - sz += elsz + uintptr(len(item.key)) + uintptr(len(item.value)) + sz += elsz + uintptr(len(item.Key())) + uintptr(len(item.Value())) } return int(sz) } @@ -55,7 +55,7 @@ func (n *node) sizeLessThan(v uintptr) bool { sz, elsz := common.PageHeaderSize, n.pageElementSize() for i := 0; i < len(n.inodes); i++ { item := &n.inodes[i] - sz += elsz + uintptr(len(item.key)) + uintptr(len(item.value)) + sz += elsz + uintptr(len(item.Key())) + uintptr(len(item.Value())) if sz >= v { return false } @@ -76,12 +76,12 @@ func (n *node) childAt(index int) *node { if n.isLeaf { panic(fmt.Sprintf("invalid childAt(%d) on a leaf node", index)) } - return n.bucket.node(n.inodes[index].pgid, n) + return n.bucket.node(n.inodes[index].Pgid(), n) } // childIndex returns the index of a given child node. func (n *node) childIndex(child *node) int { - index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, child.key) != -1 }) + index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].Key(), child.key) != -1 }) return index } @@ -125,30 +125,30 @@ func (n *node) put(oldKey, newKey, value []byte, pgId common.Pgid, flags uint32) } // Find insertion index. - index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, oldKey) != -1 }) + 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) + exact := len(n.inodes) > 0 && index < len(n.inodes) && bytes.Equal(n.inodes[index].Key(), oldKey) if !exact { - n.inodes = append(n.inodes, inode{}) + n.inodes = append(n.inodes, common.Inode{}) copy(n.inodes[index+1:], n.inodes[index:]) } inode := &n.inodes[index] - inode.flags = flags - inode.key = newKey - inode.value = value - inode.pgid = pgId - common.Assert(len(inode.key) > 0, "put: zero-length inode key") + inode.SetFlags(flags) + inode.SetKey(newKey) + inode.SetValue(value) + inode.SetPgid(pgId) + common.Assert(len(inode.Key()) > 0, "put: zero-length inode key") } // del removes a key from the node. func (n *node) del(key []byte) { // Find index of key. - index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].key, key) != -1 }) + index := sort.Search(len(n.inodes), func(i int) bool { return bytes.Compare(n.inodes[i].Key(), key) != -1 }) // Exit if the key isn't found. - if index >= len(n.inodes) || !bytes.Equal(n.inodes[index].key, key) { + if index >= len(n.inodes) || !bytes.Equal(n.inodes[index].Key(), key) { return } @@ -163,26 +163,26 @@ func (n *node) del(key []byte) { func (n *node) read(p *common.Page) { n.pgid = p.Id() n.isLeaf = (p.Flags() & common.LeafPageFlag) != 0 - n.inodes = make(inodes, int(p.Count())) + n.inodes = make(common.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.flags = elem.Flags() - inode.key = elem.Key() - inode.value = elem.Value() + inode.SetFlags(elem.Flags()) + inode.SetKey(elem.Key()) + inode.SetValue(elem.Value()) } else { elem := p.BranchPageElement(uint16(i)) - inode.pgid = elem.Pgid() - inode.key = elem.Key() + inode.SetPgid(elem.Pgid()) + inode.SetKey(elem.Key()) } - common.Assert(len(inode.key) > 0, "read: zero-length inode key") + common.Assert(len(inode.Key()) > 0, "read: zero-length inode 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 + n.key = n.inodes[0].Key() common.Assert(len(n.key) > 0, "read: zero-length node key") } else { n.key = nil @@ -216,11 +216,11 @@ func (n *node) write(p *common.Page) { // off tracks the offset into p of the start of the next data. off := unsafe.Sizeof(*p) + n.pageElementSize()*uintptr(len(n.inodes)) for i, item := range n.inodes { - common.Assert(len(item.key) > 0, "write: zero-length inode key") + common.Assert(len(item.Key()) > 0, "write: zero-length inode key") // Create a slice to write into of needed size and advance // byte pointer for next iteration. - sz := len(item.key) + len(item.value) + sz := len(item.Key()) + len(item.Value()) b := common.UnsafeByteSlice(unsafe.Pointer(p), off, 0, sz) off += uintptr(sz) @@ -228,20 +228,20 @@ func (n *node) write(p *common.Page) { if n.isLeaf { elem := p.LeafPageElement(uint16(i)) elem.SetPos(uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem)))) - elem.SetFlags(item.flags) - elem.SetKsize(uint32(len(item.key))) - elem.SetVsize(uint32(len(item.value))) + elem.SetFlags(item.Flags()) + elem.SetKsize(uint32(len(item.Key()))) + elem.SetVsize(uint32(len(item.Value()))) } else { elem := p.BranchPageElement(uint16(i)) elem.SetPos(uint32(uintptr(unsafe.Pointer(&b[0])) - uintptr(unsafe.Pointer(elem)))) - elem.SetKsize(uint32(len(item.key))) - elem.SetPgid(item.pgid) + elem.SetKsize(uint32(len(item.Key()))) + elem.SetPgid(item.Pgid()) common.Assert(elem.Pgid() != p.Id(), "write: circular dependency occurred") } // Write data for the element to the end of the page. - l := copy(b, item.key) - copy(b[l:], item.value) + l := copy(b, item.Key()) + copy(b[l:], item.Value()) } // DEBUG ONLY: n.dump() @@ -321,7 +321,7 @@ func (n *node) splitIndex(threshold int) (index, sz uintptr) { for i := 0; i < len(n.inodes)-common.MinKeysPerPage; i++ { index = uintptr(i) inode := n.inodes[i] - elsize := n.pageElementSize() + uintptr(len(inode.key)) + uintptr(len(inode.value)) + elsize := n.pageElementSize() + uintptr(len(inode.Key())) + uintptr(len(inode.Value())) // If we have at least the minimum number of keys and adding another // node would put us over the threshold then exit and return. @@ -384,11 +384,11 @@ func (n *node) spill() error { if node.parent != nil { var key = node.key if key == nil { - key = node.inodes[0].key + key = node.inodes[0].Key() } - node.parent.put(key, node.inodes[0].key, nil, node.pgid, 0) - node.key = node.inodes[0].key + node.parent.put(key, node.inodes[0].Key(), nil, node.pgid, 0) + node.key = node.inodes[0].Key() common.Assert(len(node.key) > 0, "spill: zero-length node key") } @@ -428,14 +428,14 @@ func (n *node) rebalance() { // If root node is a branch and only has one node then collapse it. if !n.isLeaf && len(n.inodes) == 1 { // Move root's child up. - child := n.bucket.node(n.inodes[0].pgid, n) + child := n.bucket.node(n.inodes[0].Pgid(), n) n.isLeaf = child.isLeaf n.inodes = child.inodes[:] n.children = child.children // Reparent all child nodes being moved. for _, inode := range n.inodes { - if child, ok := n.bucket.nodes[inode.pgid]; ok { + if child, ok := n.bucket.nodes[inode.Pgid()]; ok { child.parent = n } } @@ -474,7 +474,7 @@ func (n *node) rebalance() { if useNextSibling { // Reparent all child nodes being moved. for _, inode := range target.inodes { - if child, ok := n.bucket.nodes[inode.pgid]; ok { + if child, ok := n.bucket.nodes[inode.Pgid()]; ok { child.parent.removeChild(child) child.parent = n child.parent.children = append(child.parent.children, child) @@ -490,7 +490,7 @@ func (n *node) rebalance() { } else { // Reparent all child nodes being moved. for _, inode := range n.inodes { - if child, ok := n.bucket.nodes[inode.pgid]; ok { + if child, ok := n.bucket.nodes[inode.Pgid()]; ok { child.parent.removeChild(child) child.parent = target child.parent.children = append(child.parent.children, child) @@ -533,14 +533,14 @@ func (n *node) dereference() { for i := range n.inodes { inode := &n.inodes[i] - key := make([]byte, len(inode.key)) - copy(key, inode.key) - inode.key = key - common.Assert(len(inode.key) > 0, "dereference: zero-length inode key") + key := make([]byte, len(inode.Key())) + copy(key, inode.Key()) + inode.SetKey(key) + common.Assert(len(inode.Key()) > 0, "dereference: zero-length inode key") - value := make([]byte, len(inode.value)) - copy(value, inode.value) - inode.value = value + value := make([]byte, len(inode.Value())) + copy(value, inode.Value()) + inode.SetValue(value) } // Recursively dereference children. @@ -596,17 +596,5 @@ type nodes []*node func (s nodes) Len() int { return len(s) } func (s nodes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s nodes) Less(i, j int) bool { - return bytes.Compare(s[i].inodes[0].key, s[j].inodes[0].key) == -1 + return bytes.Compare(s[i].inodes[0].Key(), s[j].inodes[0].Key()) == -1 } - -// 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 { - flags uint32 - pgid common.Pgid - key []byte - value []byte -} - -type inodes []inode diff --git a/node_test.go b/node_test.go index 6d286e9..ce36bf0 100644 --- a/node_test.go +++ b/node_test.go @@ -11,7 +11,7 @@ import ( func TestNode_put(t *testing.T) { m := &common.Meta{} m.SetPgid(1) - n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: m}}} + n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{meta: m}}} n.put([]byte("baz"), []byte("baz"), []byte("2"), 0, 0) n.put([]byte("foo"), []byte("foo"), []byte("0"), 0, 0) n.put([]byte("bar"), []byte("bar"), []byte("1"), 0, 0) @@ -20,17 +20,17 @@ func TestNode_put(t *testing.T) { if len(n.inodes) != 3 { t.Fatalf("exp=3; got=%d", len(n.inodes)) } - if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "1" { + if k, v := n.inodes[0].Key(), n.inodes[0].Value(); string(k) != "bar" || string(v) != "1" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "baz" || string(v) != "2" { + if k, v := n.inodes[1].Key(), n.inodes[1].Value(); string(k) != "baz" || string(v) != "2" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if k, v := n.inodes[2].key, n.inodes[2].value; string(k) != "foo" || string(v) != "3" { + if k, v := n.inodes[2].Key(), n.inodes[2].Value(); string(k) != "foo" || string(v) != "3" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if n.inodes[2].flags != uint32(common.LeafPageFlag) { - t.Fatalf("not a leaf: %d", n.inodes[2].flags) + if n.inodes[2].Flags() != uint32(common.LeafPageFlag) { + t.Fatalf("not a leaf: %d", n.inodes[2].Flags()) } } @@ -64,10 +64,10 @@ func TestNode_read_LeafPage(t *testing.T) { if len(n.inodes) != 2 { t.Fatalf("exp=2; got=%d", len(n.inodes)) } - if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "fooz" { + if k, v := n.inodes[0].Key(), n.inodes[0].Value(); string(k) != "bar" || string(v) != "fooz" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "helloworld" || string(v) != "bye" { + if k, v := n.inodes[1].Key(), n.inodes[1].Value(); string(k) != "helloworld" || string(v) != "bye" { t.Fatalf("exp=; got=<%s,%s>", k, v) } } @@ -77,7 +77,7 @@ func TestNode_write_LeafPage(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) - n := &node{isLeaf: true, inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} + n := &node{isLeaf: true, inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("susy"), []byte("susy"), []byte("que"), 0, 0) n.put([]byte("ricki"), []byte("ricki"), []byte("lake"), 0, 0) n.put([]byte("john"), []byte("john"), []byte("johnson"), 0, 0) @@ -95,13 +95,13 @@ func TestNode_write_LeafPage(t *testing.T) { if len(n2.inodes) != 3 { t.Fatalf("exp=3; got=%d", len(n2.inodes)) } - if k, v := n2.inodes[0].key, n2.inodes[0].value; string(k) != "john" || string(v) != "johnson" { + if k, v := n2.inodes[0].Key(), n2.inodes[0].Value(); string(k) != "john" || string(v) != "johnson" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if k, v := n2.inodes[1].key, n2.inodes[1].value; string(k) != "ricki" || string(v) != "lake" { + if k, v := n2.inodes[1].Key(), n2.inodes[1].Value(); string(k) != "ricki" || string(v) != "lake" { t.Fatalf("exp=; got=<%s,%s>", k, v) } - if k, v := n2.inodes[2].key, n2.inodes[2].value; string(k) != "susy" || string(v) != "que" { + if k, v := n2.inodes[2].Key(), n2.inodes[2].Value(); string(k) != "susy" || string(v) != "que" { t.Fatalf("exp=; got=<%s,%s>", k, v) } } @@ -111,7 +111,7 @@ func TestNode_split(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) - n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} + n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) @@ -138,7 +138,7 @@ func TestNode_split_MinKeys(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) - n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} + n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) @@ -154,7 +154,7 @@ func TestNode_split_SinglePage(t *testing.T) { // Create a node. m := &common.Meta{} m.SetPgid(1) - n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} + n := &node{inodes: make(common.Inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: m}}} n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0)