mirror of
https://github.com/arnaucube/arbo.git
synced 2026-01-06 22:11:28 +01:00
Implement Virtual Tree construction
VirtualTree (vt) computes a tree without computing any hash. With the idea of once all the leafs are placed in their positions, the hashes can be computed, avoiding computing a node hash more than one time.
This commit is contained in:
179
vt.go
Normal file
179
vt.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package arbo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
l *node
|
||||
r *node
|
||||
k []byte
|
||||
v []byte
|
||||
path []bool
|
||||
h []byte
|
||||
}
|
||||
|
||||
type params struct {
|
||||
maxLevels int
|
||||
hashFunction HashFunction
|
||||
emptyHash []byte
|
||||
}
|
||||
|
||||
// vt stands for virtual tree. It's a tree that does not have any computed hash
|
||||
// while placing the leafs. Once all the leafs are placed, it computes all the
|
||||
// hashes. In this way, each node hash is only computed one time.
|
||||
type vt struct {
|
||||
root *node
|
||||
params *params
|
||||
}
|
||||
|
||||
func newVT(maxLevels int, hash HashFunction) vt {
|
||||
return vt{
|
||||
root: nil,
|
||||
params: ¶ms{
|
||||
maxLevels: maxLevels,
|
||||
hashFunction: hash,
|
||||
emptyHash: make([]byte, hash.Len()), // empty
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *vt) add(k, v []byte) error {
|
||||
leaf := newLeafNode(t.params, k, v)
|
||||
if t.root == nil {
|
||||
t.root = leaf
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := t.root.add(t.params, 0, leaf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newLeafNode(p *params, k, v []byte) *node {
|
||||
keyPath := make([]byte, p.hashFunction.Len())
|
||||
copy(keyPath[:], k)
|
||||
path := getPath(p.maxLevels, keyPath)
|
||||
n := &node{
|
||||
k: k,
|
||||
v: v,
|
||||
path: path,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type virtualNodeType int
|
||||
|
||||
const (
|
||||
vtEmpty = 0 // for convenience uses same value that PrefixValueEmpty
|
||||
vtLeaf = 1 // for convenience uses same value that PrefixValueLeaf
|
||||
vtMid = 2 // for convenience uses same value that PrefixValueIntermediate
|
||||
)
|
||||
|
||||
func (n *node) typ() virtualNodeType {
|
||||
if n.l == nil && n.r == nil && n.k != nil {
|
||||
return vtLeaf
|
||||
}
|
||||
if n.l != nil || n.r != nil {
|
||||
return vtMid
|
||||
}
|
||||
return vtEmpty
|
||||
}
|
||||
|
||||
func (n *node) add(p *params, currLvl int, leaf *node) error {
|
||||
if currLvl > p.maxLevels-1 {
|
||||
return fmt.Errorf("max virtual level %d", currLvl)
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
// n = leaf // TMP!
|
||||
return nil
|
||||
}
|
||||
|
||||
t := n.typ()
|
||||
switch t {
|
||||
case vtMid:
|
||||
if leaf.path[currLvl] {
|
||||
//right
|
||||
if n.r == nil {
|
||||
// empty sub-node, add the leaf here
|
||||
n.r = leaf
|
||||
}
|
||||
if err := n.r.add(p, currLvl+1, leaf); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if n.l == nil {
|
||||
// empty sub-node, add the leaf here
|
||||
n.l = leaf
|
||||
}
|
||||
if err := n.l.add(p, currLvl+1, leaf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case vtLeaf:
|
||||
if bytes.Equal(n.k, leaf.k) {
|
||||
return fmt.Errorf("key already exists")
|
||||
}
|
||||
|
||||
oldLeaf := &node{
|
||||
k: n.k,
|
||||
v: n.v,
|
||||
path: n.path,
|
||||
}
|
||||
// remove values from current node (converting it to mid node)
|
||||
n.k = nil
|
||||
n.v = nil
|
||||
n.path = nil
|
||||
if err := n.downUntilDivergence(p, currLvl, oldLeaf, leaf); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("ERR")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) downUntilDivergence(p *params, currLvl int, oldLeaf, newLeaf *node) error {
|
||||
if currLvl > p.maxLevels-1 {
|
||||
return fmt.Errorf("max virtual level %d", currLvl)
|
||||
}
|
||||
|
||||
// if oldLeaf.path[currLvl+1] != newLeaf.path[currLvl+1] {
|
||||
if oldLeaf.path[currLvl] != newLeaf.path[currLvl] {
|
||||
// reached divergence in next level
|
||||
// if newLeaf.path[currLvl+1] {
|
||||
if newLeaf.path[currLvl] {
|
||||
n.l = oldLeaf
|
||||
n.r = newLeaf
|
||||
} else {
|
||||
n.l = newLeaf
|
||||
n.r = oldLeaf
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// no divergence yet, continue going down
|
||||
if newLeaf.path[currLvl] {
|
||||
// right
|
||||
n.r = &node{}
|
||||
if err := n.r.downUntilDivergence(p, currLvl+1, oldLeaf, newLeaf); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// left
|
||||
n.l = &node{}
|
||||
if err := n.l.downUntilDivergence(p, currLvl+1, oldLeaf, newLeaf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) computeHashes() ([]kv, error) {
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user