From c441ccdf01efdbbb11ea5116b58dc76672558e06 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Sun, 1 Apr 2018 18:00:28 +0200 Subject: [PATCH] added more test --- store/cachekvstore.go | 11 ++++++++ store/dbstoreadapter.go | 8 ++++++ store/iavlstore.go | 34 ++++++++---------------- store/iavlstore_test.go | 59 +++++++++++++++++++++++++++++++++++++++++ types/store.go | 33 +++++++++++++++++++++++ 5 files changed, 122 insertions(+), 23 deletions(-) diff --git a/store/cachekvstore.go b/store/cachekvstore.go index e075e7847..f6b0e7a5e 100644 --- a/store/cachekvstore.go +++ b/store/cachekvstore.go @@ -5,6 +5,7 @@ import ( "sort" "sync" + sdk "github.com/cosmos/cosmos-sdk/types" cmn "github.com/tendermint/tmlibs/common" ) @@ -134,6 +135,16 @@ func (ci *cacheKVStore) ReverseIterator(start, end []byte) Iterator { return ci.iterator(start, end, false) } +// Implements KVStore. +func (ci *cacheKVStore) Subspace(prefix []byte) Iterator { + return ci.iterator(prefix, sdk.PrefixEndBytes(prefix), true) +} + +// Implements KVStore. +func (ci *cacheKVStore) ReverseSubspace(prefix []byte) Iterator { + return ci.iterator(prefix, sdk.PrefixEndBytes(prefix), false) +} + func (ci *cacheKVStore) iterator(start, end []byte, ascending bool) Iterator { var parent, cache Iterator if ascending { diff --git a/store/dbstoreadapter.go b/store/dbstoreadapter.go index a1b580fd4..39309aaf2 100644 --- a/store/dbstoreadapter.go +++ b/store/dbstoreadapter.go @@ -19,5 +19,13 @@ func (dsa dbStoreAdapter) CacheWrap() CacheWrap { return NewCacheKVStore(dsa) } +func (dsa dbStoreAdapter) Subspace(prefix []byte) Iterator { + return dsa.Iterator(prefix, sdk.PrefixEndBytes(prefix)) +} + +func (dsa dbStoreAdapter) ReverseSubspace(prefix []byte) Iterator { + return dsa.ReverseIterator(prefix, sdk.PrefixEndBytes(prefix)) +} + // dbm.DB implements KVStore so we can CacheKVStore it. var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/store/iavlstore.go b/store/iavlstore.go index 330358d8d..b27aacb2b 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -119,33 +119,21 @@ func (st *iavlStore) Iterator(start, end []byte) Iterator { return newIAVLIterator(st.tree.Tree(), start, end, true) } -func (st *iavlStore) Subspace(prefix []byte) Iterator { - end := make([]byte, len(prefix)) - copy(end, prefix) - finished := false - i := 1 - - for !finished { - if end[len(end)-i] != byte(255) { - end[len(end)-i]++ - finished = true - } else { - end[len(end)-i]++ - i++ - if i > len(end) { - end = nil - finished = true - } - } - } - return st.Iterator(prefix, end) -} - -// Implements IterKVStore. +// Implements KVStore. func (st *iavlStore) ReverseIterator(start, end []byte) Iterator { return newIAVLIterator(st.tree.Tree(), start, end, false) } +// Implements KVStore. +func (st *iavlStore) Subspace(prefix []byte) Iterator { + return st.Iterator(prefix, sdk.PrefixEndBytes(prefix)) +} + +// Implements KVStore. +func (st *iavlStore) ReverseSubspace(prefix []byte) Iterator { + return st.ReverseIterator(prefix, sdk.PrefixEndBytes(prefix)) +} + // Query implements ABCI interface, allows queries // // by default we will return from (latest height -1), diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 406d26926..2c4c0ea00 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -198,6 +198,65 @@ func TestIAVLSubspace(t *testing.T) { assert.Equal(t, len(expected), i) } +func TestIAVLReverseSubspace(t *testing.T) { + db := dbm.NewMemDB() + tree, _ := newTree(t, db) + iavlStore := newIAVLStore(tree, numHistory) + + iavlStore.Set([]byte("test1"), []byte("test1")) + iavlStore.Set([]byte("test2"), []byte("test2")) + iavlStore.Set([]byte("test3"), []byte("test3")) + iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(0)}, []byte("test4")) + iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(1)}, []byte("test4")) + iavlStore.Set([]byte{byte(55), byte(255), byte(255), byte(255)}, []byte("test4")) + iavlStore.Set([]byte{byte(255), byte(255), byte(0)}, []byte("test4")) + iavlStore.Set([]byte{byte(255), byte(255), byte(1)}, []byte("test4")) + iavlStore.Set([]byte{byte(255), byte(255), byte(255)}, []byte("test4")) + + i := 0 + + iter := iavlStore.ReverseSubspace([]byte("test")) + expected := []string{"test3", "test2", "test1"} + for i = 0; iter.Valid(); iter.Next() { + expectedKey := expected[i] + key, value := iter.Key(), iter.Value() + assert.EqualValues(t, key, expectedKey) + assert.EqualValues(t, value, expectedKey) + i += 1 + } + assert.Equal(t, len(expected), i) + + iter = iavlStore.ReverseSubspace([]byte{byte(55), byte(255), byte(255)}) + expected2 := [][]byte{ + []byte{byte(55), byte(255), byte(255), byte(255)}, + []byte{byte(55), byte(255), byte(255), byte(1)}, + []byte{byte(55), byte(255), byte(255), byte(0)}, + } + for i = 0; iter.Valid(); iter.Next() { + expectedKey := expected2[i] + key, value := iter.Key(), iter.Value() + assert.EqualValues(t, key, expectedKey) + assert.EqualValues(t, value, []byte("test4")) + i += 1 + } + assert.Equal(t, len(expected), i) + + iter = iavlStore.ReverseSubspace([]byte{byte(255), byte(255)}) + expected2 = [][]byte{ + []byte{byte(255), byte(255), byte(255)}, + []byte{byte(255), byte(255), byte(1)}, + []byte{byte(255), byte(255), byte(0)}, + } + for i = 0; iter.Valid(); iter.Next() { + expectedKey := expected2[i] + key, value := iter.Key(), iter.Value() + assert.EqualValues(t, key, expectedKey) + assert.EqualValues(t, value, []byte("test4")) + i += 1 + } + assert.Equal(t, len(expected), i) +} + func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewVersionedTree(db, cacheSize) diff --git a/types/store.go b/types/store.go index ef2e9154b..9034ea6f3 100644 --- a/types/store.go +++ b/types/store.go @@ -111,6 +111,14 @@ type KVStore interface { // CONTRACT: No writes may happen within a domain while an iterator exists over it. ReverseIterator(start, end []byte) Iterator + // Iterator over all the keys with a certain prefix in ascending order. + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + Subspace(prefix []byte) Iterator + + // Iterator over all the keys with a certain prefix in descending order. + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + ReverseSubspace(prefix []byte) Iterator + // TODO Not yet implemented. // CreateSubKVStore(key *storeKey) (KVStore, error) @@ -222,3 +230,28 @@ func (key *KVStoreKey) Name() string { func (key *KVStoreKey) String() string { return fmt.Sprintf("KVStoreKey{%p, %s}", key, key.name) } + +// TODO: Move to TmLibs +func PrefixEndBytes(prefix []byte) []byte { + if prefix == nil { + return nil + } + + end := make([]byte, len(prefix)) + copy(end, prefix) + finished := false + + for !finished { + if end[len(end)-1] != byte(255) { + end[len(end)-1]++ + finished = true + } else { + end = end[:len(end)-1] + if len(end) == 0 { + end = nil + finished = true + } + } + } + return end +}