mirror of
https://github.com/arnaucube/arbo.git
synced 2026-01-09 07:21:28 +01:00
Update check of already existing key on add
- Update check of already existing key on add - Update docs of methods
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
35
tree.go
35
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
|
fmt.Printf("newKey: %s, currKey: %s, currLvl: %d, currValue: %s\n",
|
||||||
// reaching this point
|
hex.EncodeToString(newKey), hex.EncodeToString(currKey),
|
||||||
// return currKey, empty, siblings, nil
|
currLvl, hex.EncodeToString(currValue))
|
||||||
panic("should not be reached, as the 'if' above should avoid reaching this point") // TMP
|
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
|
||||||
|
|||||||
@@ -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.Assert(err, qt.IsNil)
|
||||||
c.Check(err, qt.Equals, ErrMaxVirtualLevel)
|
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) {
|
||||||
|
|||||||
8
vt.go
8
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user