From 9ceef7123c7ff05e643abad77c74f31b8a664230 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Tue, 7 Mar 2023 09:03:34 +0100 Subject: [PATCH] Inline Iterator interface --- go.mod | 2 +- go.sum | 3 +- internal/api/iterator.go | 8 +++--- internal/api/mocks.go | 4 +-- types/store.go | 61 +++++++++++++++++++++++++++++++++++----- 5 files changed, 62 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index eb4711b5a..25dc15cb6 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/golang/snappy v0.0.1 // indirect github.com/google/btree v1.0.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect go.etcd.io/bbolt v1.3.6 // indirect diff --git a/go.sum b/go.sum index 159c9ac2c..d4d5019eb 100644 --- a/go.sum +++ b/go.sum @@ -81,9 +81,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= diff --git a/internal/api/iterator.go b/internal/api/iterator.go index 6e30d1824..a29b1d07b 100644 --- a/internal/api/iterator.go +++ b/internal/api/iterator.go @@ -4,11 +4,11 @@ import ( "fmt" "sync" - dbm "github.com/tendermint/tm-db" + "github.com/CosmWasm/wasmvm/types" ) // frame stores all Iterators for one contract call -type frame []dbm.Iterator +type frame []types.Iterator // iteratorFrames contains one frame for each contract call, indexed by contract call ID. var ( @@ -56,7 +56,7 @@ func endCall(callID uint64) { // storeIterator will add this to the end of the frame for the given ID and return a reference to it. // We start counting with 1, so the 0 value is flagged as an error. This means we must // remember to do idx-1 when retrieving -func storeIterator(callID uint64, it dbm.Iterator, frameLenLimit int) (uint64, error) { +func storeIterator(callID uint64, it types.Iterator, frameLenLimit int) (uint64, error) { iteratorFramesMutex.Lock() defer iteratorFramesMutex.Unlock() @@ -75,7 +75,7 @@ func storeIterator(callID uint64, it dbm.Iterator, frameLenLimit int) (uint64, e // retrieveIterator will recover an iterator based on index. This ensures it will not be garbage collected. // We start counting with 1, in storeIterator so the 0 value is flagged as an error. This means we must // remember to do idx-1 when retrieving -func retrieveIterator(callID uint64, index uint64) dbm.Iterator { +func retrieveIterator(callID uint64, index uint64) types.Iterator { iteratorFramesMutex.Lock() defer iteratorFramesMutex.Unlock() myFrame := iteratorFrames[callID] diff --git a/internal/api/mocks.go b/internal/api/mocks.go index d0795e9bb..2a067b404 100644 --- a/internal/api/mocks.go +++ b/internal/api/mocks.go @@ -305,7 +305,7 @@ func (l Lookup) Delete(key []byte) { } // Iterator wraps the underlying DB's Iterator method panicing on error. -func (l Lookup) Iterator(start, end []byte) dbm.Iterator { +func (l Lookup) Iterator(start, end []byte) types.Iterator { l.meter.ConsumeGas(RangePrice, "range") iter, err := l.db.Iterator(start, end) if err != nil { @@ -316,7 +316,7 @@ func (l Lookup) Iterator(start, end []byte) dbm.Iterator { } // ReverseIterator wraps the underlying DB's ReverseIterator method panicing on error. -func (l Lookup) ReverseIterator(start, end []byte) dbm.Iterator { +func (l Lookup) ReverseIterator(start, end []byte) types.Iterator { l.meter.ConsumeGas(RangePrice, "range") iter, err := l.db.ReverseIterator(start, end) if err != nil { diff --git a/types/store.go b/types/store.go index fe0fb40cb..f767dae3e 100644 --- a/types/store.go +++ b/types/store.go @@ -1,9 +1,5 @@ package types -import ( - dbm "github.com/tendermint/tm-db" -) - // KVStore copies a subset of types from cosmos-sdk // We may wish to make this more generic sometime in the future, but not now // https://github.com/cosmos/cosmos-sdk/blob/bef3689245bab591d7d169abd6bea52db97a70c7/store/types/store.go#L170 @@ -16,10 +12,61 @@ type KVStore interface { // Start must be less than end, or the Iterator is invalid. // Iterator must be closed by caller. // To iterate over entire domain, use store.Iterator(nil, nil) - Iterator(start, end []byte) dbm.Iterator + Iterator(start, end []byte) Iterator - // Iterator over a domain of keys in descending order. End is exclusive. + // ReverseIterator iterator over a domain of keys in descending order. End is exclusive. // Start must be less than end, or the Iterator is invalid. // Iterator must be closed by caller. - ReverseIterator(start, end []byte) dbm.Iterator + ReverseIterator(start, end []byte) Iterator +} + +// Iterator represents an iterator over a domain of keys. Callers must call Close when done. +// No writes can happen to a domain while there exists an iterator over it, some backends may take +// out database locks to ensure this will not happen. +// +// Callers must make sure the iterator is valid before calling any methods on it, otherwise +// these methods will panic. This is in part caused by most backend databases using this convention. +// +// As with DB, keys and values should be considered read-only, and must be copied before they are +// modified. +// +// Typical usage: +// +// var itr Iterator = ... +// defer itr.Close() +// +// for ; itr.Valid(); itr.Next() { +// k, v := itr.Key(); itr.Value() +// ... +// } +// +// if err := itr.Error(); err != nil { +// ... +// } +type Iterator interface { + // Domain returns the start (inclusive) and end (exclusive) limits of the iterator. + // CONTRACT: start, end readonly []byte + Domain() (start []byte, end []byte) + + // Valid returns whether the current iterator is valid. Once invalid, the Iterator remains + // invalid forever. + Valid() bool + + // Next moves the iterator to the next key in the database, as defined by order of iteration. + // If Valid returns false, this method will panic. + Next() + + // Key returns the key at the current position. Panics if the iterator is invalid. + // CONTRACT: key readonly []byte + Key() (key []byte) + + // Value returns the value at the current position. Panics if the iterator is invalid. + // CONTRACT: value readonly []byte + Value() (value []byte) + + // Error returns the last error encountered by the iterator, if any. + Error() error + + // Close closes the iterator, relasing any allocated resources. + Close() error }