diff --git a/vt.go b/vt.go new file mode 100644 index 0000000..81a05d0 --- /dev/null +++ b/vt.go @@ -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 +}