| 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 | |
| }
 |