Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

porting red black tree to optimize the key/value #59

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.20
require (
github.com/JekaMas/go-mutesting v1.1.2
github.com/cockroachdb/pebble v0.0.0-20221111210721-1bda21f14fc2
github.com/emirpasic/gods v1.12.0
github.com/ethereum/go-ethereum v1.9.22
github.com/golang/mock v1.6.0
github.com/hashicorp/golang-lru v0.5.4
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down
28 changes: 14 additions & 14 deletions kvdb/flushable/flushable.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"errors"
"sync"

rbt "github.com/emirpasic/gods/trees/redblacktree"
"github.com/ethereum/go-ethereum/common"

"github.com/Fantom-foundation/lachesis-base/kvdb"
rbt "github.com/Fantom-foundation/lachesis-base/kvdb/redblacktree"
)

var (
Expand Down Expand Up @@ -53,7 +53,7 @@ func WrapWithDrop(parent kvdb.Store, drop func()) *Flushable {
return &Flushable{
flushableReader: flushableReader{
underlying: parent,
modified: rbt.NewWithStringComparator(),
modified: rbt.New(),
},
onDrop: drop,
underlying: parent,
Expand All @@ -78,7 +78,7 @@ func (w *Flushable) Put(key []byte, value []byte) error {
}

func (w *Flushable) put(key []byte, value []byte) {
w.modified.Put(string(key), common.CopyBytes(value))
w.modified.Put(common.CopyBytes(key), common.CopyBytes(value))
*w.sizeEstimation += len(key) + len(value) + 128
}

Expand All @@ -91,7 +91,7 @@ func (w *flushableReader) Has(key []byte) (bool, error) {
return false, errClosed
}

val, ok := w.modified.Get(string(key))
val, ok := w.modified.Get(common.CopyBytes(key))
if ok {
return val != nil, nil
}
Expand All @@ -108,11 +108,11 @@ func (w *flushableReader) Get(key []byte) ([]byte, error) {
return nil, errClosed
}

if entry, ok := w.modified.Get(string(key)); ok {
if entry, ok := w.modified.Get(common.CopyBytes(key)); ok {
if entry == nil {
return nil, nil
}
return common.CopyBytes(entry.([]byte)), nil
return common.CopyBytes(entry), nil
}

return w.underlying.Get(key)
Expand All @@ -128,7 +128,7 @@ func (w *Flushable) Delete(key []byte) error {
}

func (w *Flushable) delete(key []byte) {
w.modified.Put(string(key), nil)
w.modified.Put(common.CopyBytes(key), nil)
*w.sizeEstimation += len(key) + 128 // it should be (len(key) - len(old value)), but we'd need to read old value
}

Expand Down Expand Up @@ -203,9 +203,9 @@ func (w *Flushable) flush() error {
var err error

if it.Value() == nil {
err = batch.Delete([]byte(it.Key().(string)))
err = batch.Delete(it.Key())
} else {
err = batch.Put([]byte(it.Key().(string)), it.Value().([]byte))
err = batch.Put(it.Key(), it.Value())
}

if err != nil {
Expand Down Expand Up @@ -270,7 +270,7 @@ func nextNode(tree *rbt.Tree, node *rbt.Node) (next *rbt.Node, ok bool) {
if node.Parent != nil {
for node.Parent != nil {
node = node.Parent
if tree.Comparator(origin.Key, node.Key) <= 0 {
if rbt.BytesComparator(origin.Key, node.Key) <= 0 {
return node, node != nil
}
}
Expand All @@ -283,11 +283,11 @@ func castToPair(node *rbt.Node) (key, val []byte) {
if node == nil {
return nil, nil
}
key = []byte(node.Key.(string))
key = node.Key
if node.Value == nil {
val = nil // deleted key
} else {
val = node.Value.([]byte) // inserted value
val = node.Value // inserted value
}
return key, val
}
Expand All @@ -296,7 +296,7 @@ func castToPair(node *rbt.Node) (key, val []byte) {
func (it *flushableIterator) init() {
it.parentOk = it.parentIt.Next()
if len(it.start) != 0 {
it.treeNode, it.treeOk = it.tree.Ceiling(string(it.start)) // not strict >=
it.treeNode, it.treeOk = it.tree.Ceiling(common.CopyBytes(it.start)) // not strict >=
} else {
it.treeNode = it.tree.Left() // lowest key
it.treeOk = it.treeNode != nil
Expand Down Expand Up @@ -411,7 +411,7 @@ func (w *Flushable) GetSnapshot() (kvdb.Snapshot, error) {
if err != nil {
return nil, err
}
modifiedCopy := rbt.NewWithStringComparator()
modifiedCopy := rbt.New()
for it := w.modified.Iterator(); it.Next(); {
modifiedCopy.Put(it.Key(), it.Value())
}
Expand Down
23 changes: 23 additions & 0 deletions kvdb/redblacktree/comparator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package redblacktree

// BytesComparator provides a basic comparison on []byte
func BytesComparator(a, b []byte) int {
min := len(b)
if len(a) < len(b) {
min = len(a)
}
diff := 0
for i := 0; i < min && diff == 0; i++ {
diff = int(a[i]) - int(b[i])
}
if diff == 0 {
diff = len(a) - len(b)
}
if diff < 0 {
return -1
}
if diff > 0 {
return 1
}
return 0
}
185 changes: 185 additions & 0 deletions kvdb/redblacktree/iterator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package redblacktree

// Iterator holding the iterator's state
type Iterator struct {
tree *Tree
node *Node
position position
}

type position byte

const (
begin, between, end position = 0, 1, 2
)

// Iterator returns a stateful iterator whose elements are key/value pairs.
func (tree *Tree) Iterator() Iterator {
return Iterator{tree: tree, node: nil, position: begin}
}

// IteratorAt returns a stateful iterator whose elements are key/value pairs that is initialised at a particular node.
func (tree *Tree) IteratorAt(node *Node) Iterator {
return Iterator{tree: tree, node: node, position: between}
}

// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
if iterator.position == end {
goto end
}
if iterator.position == begin {
left := iterator.tree.Left()
if left == nil {
goto end
}
iterator.node = left
goto between
}
if iterator.node.Right != nil {
iterator.node = iterator.node.Right
for iterator.node.Left != nil {
iterator.node = iterator.node.Left
}
goto between
}
for iterator.node.Parent != nil {
node := iterator.node
iterator.node = iterator.node.Parent
if node == iterator.node.Left {
goto between
}
}

end:
iterator.node = nil
iterator.position = end
return false

between:
iterator.position = between
return true
}

// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.position == begin {
goto begin
}
if iterator.position == end {
right := iterator.tree.Right()
if right == nil {
goto begin
}
iterator.node = right
goto between
}
if iterator.node.Left != nil {
iterator.node = iterator.node.Left
for iterator.node.Right != nil {
iterator.node = iterator.node.Right
}
goto between
}
for iterator.node.Parent != nil {
node := iterator.node
iterator.node = iterator.node.Parent
if node == iterator.node.Right {
goto between
}
}

begin:
iterator.node = nil
iterator.position = begin
return false

between:
iterator.position = between
return true
}

// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() []byte {
return iterator.node.Value
}

// Key returns the current element's key.
// Does not modify the state of the iterator.
func (iterator *Iterator) Key() []byte {
return iterator.node.Key
}

// Node returns the current element's node.
// Does not modify the state of the iterator.
func (iterator *Iterator) Node() *Node {
return iterator.node
}

// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
func (iterator *Iterator) Begin() {
iterator.node = nil
iterator.position = begin
}

// End moves the iterator past the last element (one-past-the-end).
// Call Prev() to fetch the last element if any.
func (iterator *Iterator) End() {
iterator.node = nil
iterator.position = end
}

// First moves the iterator to the first element and returns true if there was a first element in the container.
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator
func (iterator *Iterator) First() bool {
iterator.Begin()
return iterator.Next()
}

// Last moves the iterator to the last element and returns true if there was a last element in the container.
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Last() bool {
iterator.End()
return iterator.Prev()
}

// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) NextTo(f func(key []byte, value []byte) bool) bool {
for iterator.Next() {
key, value := iterator.Key(), iterator.Value()
if f(key, value) {
return true
}
}
return false
}

// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) PrevTo(f func(key []byte, value []byte) bool) bool {
for iterator.Prev() {
key, value := iterator.Key(), iterator.Value()
if f(key, value) {
return true
}
}
return false
}
Loading