Browse Source

Update check of already existing key on add

- Update check of already existing key on add
- Update docs of methods
master
arnaucube 3 years ago
parent
commit
467f063129
4 changed files with 38 additions and 18 deletions
  1. +4
    -0
      addbatch_test.go
  2. +24
    -11
      tree.go
  3. +4
    -5
      tree_test.go
  4. +6
    -2
      vt.go

+ 4
- 0
addbatch_test.go

@ -5,6 +5,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/big" "math/big"
"os"
"runtime" "runtime"
"sort" "sort"
"testing" "testing"
@ -660,6 +661,7 @@ func benchAdd(t *testing.T, ks, vs [][]byte) {
c := qt.New(t) c := qt.New(t)
dbDir := t.TempDir() dbDir := t.TempDir()
defer os.RemoveAll(dbDir) //nolint:errcheck
// storage, err := pebble.NewPebbleStorage(dbDir, false) // storage, err := pebble.NewPebbleStorage(dbDir, false)
storage, err := leveldb.NewLevelDbStorage(dbDir, false) storage, err := leveldb.NewLevelDbStorage(dbDir, false)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -681,6 +683,8 @@ func benchAddBatch(t *testing.T, ks, vs [][]byte) {
c := qt.New(t) c := qt.New(t)
dbDir := t.TempDir() dbDir := t.TempDir()
defer os.RemoveAll(dbDir) //nolint:errcheck
// storage, err := pebble.NewPebbleStorage(dbDir, false)
storage, err := leveldb.NewLevelDbStorage(dbDir, false) storage, err := leveldb.NewLevelDbStorage(dbDir, false)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
tree, err := NewTree(storage, 140, HashFunctionBlake2b) tree, err := NewTree(storage, 140, HashFunctionBlake2b)

+ 24
- 11
tree.go

@ -278,23 +278,24 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte,
switch currValue[0] { switch currValue[0] {
case PrefixValueEmpty: // empty case PrefixValueEmpty: // empty
// TODO WIP WARNING should not be reached, as the 'if' above should avoid
// reaching this point
// return currKey, empty, siblings, nil
panic("should not be reached, as the 'if' above should avoid reaching this point") // TMP
fmt.Printf("newKey: %s, currKey: %s, currLvl: %d, currValue: %s\n",
hex.EncodeToString(newKey), hex.EncodeToString(currKey),
currLvl, hex.EncodeToString(currValue))
panic("This point should not be reached, as the 'if' above" +
" should avoid reaching this point. This panic is temporary" +
" for reporting purposes, will be deleted in future versions." +
" Please paste this log (including the previous lines) in a" +
" new issue: https://github.com/arnaucube/arbo/issues/new") // TMP
case PrefixValueLeaf: // leaf case PrefixValueLeaf: // leaf
if bytes.Equal(newKey, currKey) {
// TODO move this error msg to const & add test that
// checks that adding a repeated key this error is
// returned
return nil, nil, nil, ErrKeyAlreadyExists
}
if !bytes.Equal(currValue, emptyValue) { if !bytes.Equal(currValue, emptyValue) {
if getLeaf { if getLeaf {
return currKey, currValue, siblings, nil return currKey, currValue, siblings, nil
} }
oldLeafKey, _ := ReadLeafValue(currValue) oldLeafKey, _ := ReadLeafValue(currValue)
if bytes.Equal(newKey, oldLeafKey) {
return nil, nil, nil, ErrKeyAlreadyExists
}
oldLeafKeyFull := make([]byte, t.hashFunction.Len()) oldLeafKeyFull := make([]byte, t.hashFunction.Len())
copy(oldLeafKeyFull[:], oldLeafKey) copy(oldLeafKeyFull[:], oldLeafKey)
@ -385,6 +386,12 @@ func (t *Tree) newLeafValue(k, v []byte) ([]byte, []byte, error) {
return newLeafValue(t.hashFunction, k, v) return newLeafValue(t.hashFunction, k, v)
} }
// newLeafValue takes a key & value from a leaf, and computes the leaf hash,
// which is used as the leaf key. And the value is the concatenation of the
// inputed key & value. The output of this function is used as key-value to
// store the leaf in the DB.
// [ 1 byte | 1 byte | N bytes | M bytes ]
// [ type of node | length of key | key | value ]
func newLeafValue(hashFunc HashFunction, k, v []byte) ([]byte, []byte, error) { func newLeafValue(hashFunc HashFunction, k, v []byte) ([]byte, []byte, error) {
leafKey, err := hashFunc.Hash(k, v, []byte{1}) leafKey, err := hashFunc.Hash(k, v, []byte{1})
if err != nil { if err != nil {
@ -418,6 +425,12 @@ func (t *Tree) newIntermediate(l, r []byte) ([]byte, []byte, error) {
return newIntermediate(t.hashFunction, l, r) return newIntermediate(t.hashFunction, l, r)
} }
// newIntermediate takes the left & right keys of a intermediate node, and
// computes its hash. Returns the hash of the node, which is the node key, and a
// byte array that contains the value (which contains the left & right child
// keys) to store in the DB.
// [ 1 byte | 1 byte | N bytes | N bytes ]
// [ type of node | length of key | left key | right key ]
func newIntermediate(hashFunc HashFunction, l, r []byte) ([]byte, []byte, error) { func newIntermediate(hashFunc HashFunction, l, r []byte) ([]byte, []byte, error) {
b := make([]byte, PrefixValueLen+hashFunc.Len()*2) b := make([]byte, PrefixValueLen+hashFunc.Len()*2)
b[0] = 2 b[0] = 2

+ 4
- 5
tree_test.go

@ -141,12 +141,11 @@ func TestAddRepeatedIndex(t *testing.T) {
bLen := tree.HashFunction().Len() bLen := tree.HashFunction().Len()
k := BigIntToBytes(bLen, big.NewInt(int64(3))) k := BigIntToBytes(bLen, big.NewInt(int64(3)))
v := BigIntToBytes(bLen, big.NewInt(int64(12))) v := BigIntToBytes(bLen, big.NewInt(int64(12)))
if err := tree.Add(k, v); err != nil {
t.Fatal(err)
}
err = tree.Add(k, v) err = tree.Add(k, v)
c.Assert(err, qt.Not(qt.IsNil))
c.Check(err, qt.Equals, ErrMaxVirtualLevel)
c.Assert(err, qt.IsNil)
err = tree.Add(k, v) // repeating same key-value
c.Check(err, qt.Equals, ErrKeyAlreadyExists)
} }
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {

+ 6
- 2
vt.go

@ -245,6 +245,7 @@ func (n *node) getNodesAtLevel(currLvl, l int) ([]*node, error) {
return nodes, nil return nodes, nil
} }
// upFromNodes builds the tree from the bottom to up
func upFromNodes(ns []*node) (*node, error) { func upFromNodes(ns []*node) (*node, error) {
if len(ns) == 1 { if len(ns) == 1 {
return ns[0], nil return ns[0], nil
@ -267,6 +268,7 @@ func upFromNodes(ns []*node) (*node, error) {
return upFromNodes(res) return upFromNodes(res)
} }
// add adds a key&value as a leaf in the VirtualTree
func (t *vt) add(fromLvl int, k, v []byte) error { func (t *vt) add(fromLvl int, k, v []byte) error {
leaf := newLeafNode(t.params, k, v) leaf := newLeafNode(t.params, k, v)
if t.root == nil { if t.root == nil {
@ -282,7 +284,8 @@ func (t *vt) add(fromLvl int, k, v []byte) error {
} }
// computeHashes should be called after all the vt.add is used, once all the // computeHashes should be called after all the vt.add is used, once all the
// leafs are in the tree
// leafs are in the tree. Computes the hashes of the tree, parallelizing in the
// available CPUs.
func (t *vt) computeHashes() ([][2][]byte, error) { func (t *vt) computeHashes() ([][2][]byte, error) {
var err error var err error
@ -519,7 +522,8 @@ func flp2(n int) int {
return res return res
} }
// returns an array of key-values to store in the db
// computeHashes computes the hashes under the node from which is called the
// method. Returns an array of key-values to store in the db
func (n *node) computeHashes(currLvl, maxLvl int, p *params, pairs [][2][]byte) ( func (n *node) computeHashes(currLvl, maxLvl int, p *params, pairs [][2][]byte) (
[][2][]byte, error) { [][2][]byte, error) {
if n == nil || currLvl >= maxLvl { if n == nil || currLvl >= maxLvl {

Loading…
Cancel
Save