-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR #5435: Added iterator that allows to read only requested values
- Loading branch information
1 parent
5188c35
commit b314b85
Showing
4 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package types | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// KVStorePrefixIteratorPaginated returns iterator over items in the selected page. | ||
// Items iterated and skipped in ascending order. | ||
func KVStorePrefixIteratorPaginated(kvs KVStore, prefix []byte, page, limit uint) Iterator { | ||
pi := &PaginatedIterator{ | ||
Iterator: KVStorePrefixIterator(kvs, prefix), | ||
page: page, | ||
limit: limit, | ||
} | ||
pi.skip() | ||
return pi | ||
} | ||
|
||
// KVStoreReversePrefixIteratorPaginated returns iterator over items in the selected page. | ||
// Items iterated and skipped in descending order. | ||
func KVStoreReversePrefixIteratorPaginated(kvs KVStore, prefix []byte, page, limit uint) Iterator { | ||
pi := &PaginatedIterator{ | ||
Iterator: KVStoreReversePrefixIterator(kvs, prefix), | ||
page: page, | ||
limit: limit, | ||
} | ||
pi.skip() | ||
return pi | ||
} | ||
|
||
// PaginatedIterator is a wrapper around Iterator that iterates over values starting for given page and limit. | ||
type PaginatedIterator struct { | ||
Iterator | ||
|
||
page, limit uint // provided during initialization | ||
iterated uint // incremented in a call to Next | ||
|
||
} | ||
|
||
func (pi *PaginatedIterator) skip() { | ||
for i := (pi.page - 1) * pi.limit; i > 0 && pi.Iterator.Valid(); i-- { | ||
pi.Iterator.Next() | ||
} | ||
} | ||
|
||
// Next will panic after limit is reached. | ||
func (pi *PaginatedIterator) Next() { | ||
if !pi.Valid() { | ||
panic(fmt.Sprintf("PaginatedIterator reached limit %d", pi.limit)) | ||
} | ||
pi.Iterator.Next() | ||
pi.iterated++ | ||
} | ||
|
||
// Valid if below limit and underlying iterator is valid. | ||
func (pi *PaginatedIterator) Valid() bool { | ||
if pi.iterated >= pi.limit { | ||
return false | ||
} | ||
return pi.Iterator.Valid() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package types_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
dbm "github.com/tendermint/tm-db" | ||
|
||
"github.com/cosmos/cosmos-sdk/store/iavl" | ||
"github.com/cosmos/cosmos-sdk/store/types" | ||
) | ||
|
||
func newMemTestKVStore(t *testing.T) types.KVStore { | ||
db := dbm.NewMemDB() | ||
store, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false) | ||
require.NoError(t, err) | ||
return store | ||
} | ||
|
||
func TestPaginatedIterator(t *testing.T) { | ||
kvs := newMemTestKVStore(t) | ||
total := 10 | ||
lth := total - 1 | ||
asc := make([][]byte, total) | ||
desc := make([][]byte, total) | ||
// store returns values in lexicographic order (or reverse lex order) | ||
for i := 0; i < total; i++ { | ||
key := []byte{byte(i)} | ||
kvs.Set(key, key) | ||
asc[i] = key | ||
desc[lth-i] = key | ||
} | ||
type testCase struct { | ||
desc string | ||
page, limit uint | ||
result [][]byte | ||
reverse bool | ||
} | ||
for _, tc := range []testCase{ | ||
{ | ||
desc: "FirstChunk", | ||
page: 1, | ||
limit: 4, | ||
result: asc[:4], | ||
}, | ||
{ | ||
desc: "SecondChunk", | ||
page: 2, | ||
limit: 4, | ||
result: asc[4:8], | ||
}, | ||
{ | ||
desc: "ThirdChunkHalf", | ||
page: 3, | ||
limit: 4, | ||
result: asc[8:], | ||
}, | ||
{ | ||
desc: "OverLimit", | ||
page: 10, | ||
limit: 10, | ||
result: [][]byte{}, | ||
}, | ||
{ | ||
desc: "ZeroLimit", | ||
page: 1, | ||
result: [][]byte{}, | ||
}, | ||
{ | ||
desc: "ReverseFirstChunk", | ||
page: 1, | ||
limit: 6, | ||
result: desc[:6], | ||
reverse: true, | ||
}, | ||
{ | ||
desc: "ReverseSecondChunk", | ||
page: 2, | ||
limit: 6, | ||
result: desc[6:], | ||
reverse: true, | ||
}, | ||
} { | ||
tc := tc | ||
t.Run(tc.desc, func(t *testing.T) { | ||
var iter types.Iterator | ||
if tc.reverse { | ||
iter = types.KVStoreReversePrefixIteratorPaginated(kvs, nil, tc.page, tc.limit) | ||
} else { | ||
iter = types.KVStorePrefixIteratorPaginated(kvs, nil, tc.page, tc.limit) | ||
} | ||
defer iter.Close() | ||
|
||
result := [][]byte{} | ||
for ; iter.Valid(); iter.Next() { | ||
result = append(result, iter.Key()) | ||
} | ||
|
||
require.Equal(t, tc.result, result) | ||
require.False(t, iter.Valid()) | ||
}) | ||
} | ||
} | ||
|
||
func TestPaginatedIteratorPanicIfInvalid(t *testing.T) { | ||
kvs := newMemTestKVStore(t) | ||
|
||
iter := types.KVStorePrefixIteratorPaginated(kvs, nil, 1, 1) | ||
defer iter.Close() | ||
require.False(t, iter.Valid()) | ||
require.Panics(t, func() { iter.Next() }) // "iterator is empty" | ||
|
||
kvs.Set([]byte{1}, []byte{}) | ||
|
||
iter = types.KVStorePrefixIteratorPaginated(kvs, nil, 1, 0) | ||
defer iter.Close() | ||
require.False(t, iter.Valid()) | ||
require.Panics(t, func() { iter.Next() }) // "not empty but limit is zero" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters