From 0c92477b4dc9bd3f704850ea6cbdc23996c7817d Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 9 Oct 2018 22:23:22 +0900 Subject: [PATCH 1/2] mv types/lib/* store/ --- {types/lib => store}/linear_test.go | 70 ++++---- store/list.go | 108 ++++++++++++ store/queue.go | 88 ++++++++++ types/lib/linear.go | 254 ---------------------------- 4 files changed, 232 insertions(+), 288 deletions(-) rename {types/lib => store}/linear_test.go (64%) create mode 100644 store/list.go create mode 100644 store/queue.go delete mode 100644 types/lib/linear.go diff --git a/types/lib/linear_test.go b/store/linear_test.go similarity index 64% rename from types/lib/linear_test.go rename to store/linear_test.go index d19c04061..c8153a041 100644 --- a/types/lib/linear_test.go +++ b/store/linear_test.go @@ -1,7 +1,7 @@ -package lib +package store import ( - "fmt" + // "math/rand" "testing" "github.com/stretchr/testify/require" @@ -12,7 +12,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -23,7 +22,7 @@ type S struct { func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) + cms := NewCommitMultiStore(db) cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) cms.LoadLatestVersion() ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) @@ -31,23 +30,11 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { return ctx, cdc } -func TestNewLinear(t *testing.T) { - cdc := codec.New() - require.NotPanics(t, func() { NewLinear(cdc, nil, nil) }) - require.NotPanics(t, func() { NewLinear(cdc, nil, DefaultLinearKeys()) }) - require.NotPanics(t, func() { NewLinear(cdc, nil, &LinearKeys{[]byte{0xAA}, []byte{0xBB}, []byte{0xCC}}) }) - - require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, nil, nil}) }) - require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{[]byte{0xAA}, nil, nil}) }) - require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, []byte{0xBB}, nil}) }) - require.Panics(t, func() { NewLinear(cdc, nil, &LinearKeys{nil, nil, []byte{0xCC}}) }) -} - func TestList(t *testing.T) { key := sdk.NewKVStoreKey("test") ctx, cdc := defaultComponents(key) store := ctx.KVStore(key) - lm := NewList(cdc, store, nil) + lm := NewList(cdc, store) val := S{1, true} var res S @@ -94,7 +81,7 @@ func TestQueue(t *testing.T) { ctx, cdc := defaultComponents(key) store := ctx.KVStore(key) - qm := NewQueue(cdc, store, nil) + qm := NewQueue(cdc, store) val := S{1, true} var res S @@ -125,20 +112,14 @@ func TestQueue(t *testing.T) { require.True(t, qm.IsEmpty()) } -func TestOptions(t *testing.T) { +func TestKeys(t *testing.T) { key := sdk.NewKVStoreKey("test") ctx, cdc := defaultComponents(key) store := ctx.KVStore(key) - - keys := &LinearKeys{ - LengthKey: []byte{0xDE, 0xAD}, - ElemKey: []byte{0xBE, 0xEF}, - TopKey: []byte{0x12, 0x34}, - } - linear := NewLinear(cdc, store, keys) + queue := NewQueue(cdc, store) for i := 0; i < 10; i++ { - linear.Push(i) + queue.Push(i) } var len uint64 @@ -147,23 +128,44 @@ func TestOptions(t *testing.T) { var actual int // Checking keys.LengthKey - err := cdc.UnmarshalBinary(store.Get(keys.LengthKey), &len) + err := cdc.UnmarshalBinary(store.Get(LengthKey()), &len) require.Nil(t, err) - require.Equal(t, len, linear.Len()) + require.Equal(t, len, queue.List.Len()) // Checking keys.ElemKey for i := 0; i < 10; i++ { - linear.Get(uint64(i), &expected) - bz := store.Get(append(keys.ElemKey, []byte(fmt.Sprintf("%020d", i))...)) + queue.List.Get(uint64(i), &expected) + bz := store.Get(ElemKey(uint64(i))) err = cdc.UnmarshalBinary(bz, &actual) require.Nil(t, err) require.Equal(t, expected, actual) } - linear.Pop() + queue.Pop() - err = cdc.UnmarshalBinary(store.Get(keys.TopKey), &top) + err = cdc.UnmarshalBinary(store.Get(TopKey()), &top) require.Nil(t, err) - require.Equal(t, top, linear.getTop()) + require.Equal(t, top, queue.getTop()) } + +/* +func TestListRandom(t *testing.T) { + key := sdk.NewKVStoreKey("test") + ctx, cdc := defaultComponents(key) + store := ctx.KVStore(key) + list := NewList(cdc, store) + mocklist := []uint32{} + + for i := 0; i < 10000; { + limit := rand.Int31() % 1000 + for j := int32(0); j < limit; j++ { + item := rand.Uint32() + list.Push(item) + mocklist = append(mocklist, item) + } + + require.Equal() + } +} +*/ diff --git a/store/list.go b/store/list.go new file mode 100644 index 000000000..9905b0d6f --- /dev/null +++ b/store/list.go @@ -0,0 +1,108 @@ +package store + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Key for the length of the list +func LengthKey() []byte { + return []byte{0x00} +} + +// Key for the elements of the list +func ElemKey(index uint64) []byte { + return append([]byte{0x01}, []byte(fmt.Sprintf("%020d", index))...) +} + +// List defines an integer indexable mapper +// It panics when the element type cannot be (un/)marshalled by the codec +type List struct { + cdc *codec.Codec + store sdk.KVStore +} + +// NewList constructs new List +func NewList(cdc *codec.Codec, store sdk.KVStore) List { + return List{ + cdc: cdc, + store: store, + } +} + +// Len() returns the length of the list +// The length is only increased by Push() and not decreased +// List dosen't check if an index is in bounds +// The user should check Len() before doing any actions +func (m List) Len() (res uint64) { + bz := m.store.Get(LengthKey()) + if bz == nil { + return 0 + } + m.cdc.MustUnmarshalBinary(bz, &res) + return +} + +// Get() returns the element by its index +func (m List) Get(index uint64, ptr interface{}) error { + bz := m.store.Get(ElemKey(index)) + return m.cdc.UnmarshalBinary(bz, ptr) +} + +// Set() stores the element to the given position +// Setting element out of range will break length counting +// Use Push() instead of Set() to append a new element +func (m List) Set(index uint64, value interface{}) { + bz := m.cdc.MustMarshalBinary(value) + m.store.Set(ElemKey(index), bz) +} + +// Delete() deletes the element in the given position +// Other elements' indices are preserved after deletion +// Panics when the index is out of range +func (m List) Delete(index uint64) { + m.store.Delete(ElemKey(index)) +} + +// Push() inserts the element to the end of the list +// It will increase the length when it is called +func (m List) Push(value interface{}) { + length := m.Len() + m.Set(length, value) + m.store.Set(LengthKey(), m.cdc.MustMarshalBinary(length+1)) +} + +// Iterate() is used to iterate over all existing elements in the list +// Return true in the continuation to break +// The second element of the continuation will indicate the position of the element +// Using it with Get() will return the same one with the provided element + +// CONTRACT: No writes may happen within a domain while iterating over it. +func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { + iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) + for ; iter.Valid(); iter.Next() { + v := iter.Value() + m.cdc.MustUnmarshalBinary(v, ptr) + k := iter.Key() + s := string(k[len(k)-20:]) + index, err := strconv.ParseUint(s, 10, 64) + if err != nil { + panic(err) + } + if fn(index) { + break + } + } + + iter.Close() +} + +func subspace(prefix []byte) (start, end []byte) { + end = make([]byte, len(prefix)) + copy(end, prefix) + end[len(end)-1]++ + return prefix, end +} diff --git a/store/queue.go b/store/queue.go new file mode 100644 index 000000000..4081f63b3 --- /dev/null +++ b/store/queue.go @@ -0,0 +1,88 @@ +package store + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Key for the top element position in the queue +func TopKey() []byte { + return []byte{0x02} +} + +// Queue is a List wrapper that provides queue-like functions +// It panics when the element type cannot be (un/)marshalled by the codec +type Queue struct { + List List +} + +// NewQueue constructs new Queue +func NewQueue(cdc *codec.Codec, store sdk.KVStore) Queue { + return Queue{NewList(cdc, store)} +} + +func (m Queue) getTop() (res uint64) { + bz := m.List.store.Get(TopKey()) + if bz == nil { + return 0 + } + + m.List.cdc.MustUnmarshalBinary(bz, &res) + return +} + +func (m Queue) setTop(top uint64) { + bz := m.List.cdc.MustMarshalBinary(top) + m.List.store.Set(TopKey(), bz) +} + +// Push() inserts the elements to the rear of the queue +func (m Queue) Push(value interface{}) { + m.List.Push(value) +} + +// Popping/Peeking on an empty queue will cause panic +// The user should check IsEmpty() before doing any actions +// Peek() returns the element at the front of the queue without removing it +func (m Queue) Peek(ptr interface{}) error { + top := m.getTop() + return m.List.Get(top, ptr) +} + +// Pop() returns the element at the front of the queue and removes it +func (m Queue) Pop() { + top := m.getTop() + m.List.Delete(top) + m.setTop(top + 1) +} + +// IsEmpty() checks if the queue is empty +func (m Queue) IsEmpty() bool { + top := m.getTop() + length := m.List.Len() + return top >= length +} + +// Flush() removes elements it processed +// Return true in the continuation to break +// The interface{} is unmarshalled before the continuation is called +// Starts from the top(head) of the queue +// CONTRACT: Pop() or Push() should not be performed while flushing +func (m Queue) Flush(ptr interface{}, fn func() bool) { + top := m.getTop() + length := m.List.Len() + + var i uint64 + for i = top; i < length; i++ { + err := m.List.Get(i, ptr) + if err != nil { + // TODO: Handle with #870 + panic(err) + } + m.List.Delete(i) + if fn() { + break + } + } + m.setTop(i) +} diff --git a/types/lib/linear.go b/types/lib/linear.go deleted file mode 100644 index 1c25f4eb4..000000000 --- a/types/lib/linear.go +++ /dev/null @@ -1,254 +0,0 @@ -package lib - -import ( - "fmt" - "strconv" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Linear defines a primitive mapper type -type Linear struct { - cdc *codec.Codec - store sdk.KVStore - keys *LinearKeys -} - -// LinearKeys defines keysions for the key bytes -type LinearKeys struct { - LengthKey []byte - ElemKey []byte - TopKey []byte -} - -// Should never be modified -var cachedDefaultLinearKeys = DefaultLinearKeys() - -// DefaultLinearKeys returns the default setting of LinearOption -func DefaultLinearKeys() *LinearKeys { - keys := LinearKeys{ - LengthKey: []byte{0x00}, - ElemKey: []byte{0x01}, - TopKey: []byte{0x02}, - } - return &keys -} - -// NewLinear constructs new Linear -func NewLinear(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) Linear { - if keys == nil { - keys = cachedDefaultLinearKeys - } - if keys.LengthKey == nil || keys.ElemKey == nil || keys.TopKey == nil { - panic("Invalid LinearKeys") - } - return Linear{ - cdc: cdc, - store: store, - keys: keys, - } -} - -// List is a Linear interface that provides list-like functions -// It panics when the element type cannot be (un/)marshalled by the codec -type List interface { - - // Len() returns the length of the list - // The length is only increased by Push() and not decreased - // List dosen't check if an index is in bounds - // The user should check Len() before doing any actions - Len() uint64 - - // Get() returns the element by its index - Get(uint64, interface{}) error - - // Set() stores the element to the given position - // Setting element out of range will break length counting - // Use Push() instead of Set() to append a new element - Set(uint64, interface{}) - - // Delete() deletes the element in the given position - // Other elements' indices are preserved after deletion - // Panics when the index is out of range - Delete(uint64) - - // Push() inserts the element to the end of the list - // It will increase the length when it is called - Push(interface{}) - - // Iterate*() is used to iterate over all existing elements in the list - // Return true in the continuation to break - // The second element of the continuation will indicate the position of the element - // Using it with Get() will return the same one with the provided element - - // CONTRACT: No writes may happen within a domain while iterating over it. - Iterate(interface{}, func(uint64) bool) -} - -// NewList constructs new List -func NewList(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) List { - return NewLinear(cdc, store, keys) -} - -// Key for the length of the list -func (m Linear) LengthKey() []byte { - return m.keys.LengthKey -} - -// Key for the elements of the list -func (m Linear) ElemKey(index uint64) []byte { - return append(m.keys.ElemKey, []byte(fmt.Sprintf("%020d", index))...) -} - -// Len implements List -func (m Linear) Len() (res uint64) { - bz := m.store.Get(m.LengthKey()) - if bz == nil { - return 0 - } - m.cdc.MustUnmarshalBinary(bz, &res) - return -} - -// Get implements List -func (m Linear) Get(index uint64, ptr interface{}) error { - bz := m.store.Get(m.ElemKey(index)) - return m.cdc.UnmarshalBinary(bz, ptr) -} - -// Set implements List -func (m Linear) Set(index uint64, value interface{}) { - bz := m.cdc.MustMarshalBinary(value) - m.store.Set(m.ElemKey(index), bz) -} - -// Delete implements List -func (m Linear) Delete(index uint64) { - m.store.Delete(m.ElemKey(index)) -} - -// Push implements List -func (m Linear) Push(value interface{}) { - length := m.Len() - m.Set(length, value) - m.store.Set(m.LengthKey(), m.cdc.MustMarshalBinary(length+1)) -} - -// IterateRead implements List -func (m Linear) Iterate(ptr interface{}, fn func(uint64) bool) { - iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) - for ; iter.Valid(); iter.Next() { - v := iter.Value() - m.cdc.MustUnmarshalBinary(v, ptr) - k := iter.Key() - s := string(k[len(k)-20:]) - index, err := strconv.ParseUint(s, 10, 64) - if err != nil { - panic(err) - } - if fn(index) { - break - } - } - - iter.Close() -} - -// Queue is a Linear interface that provides queue-like functions -// It panics when the element type cannot be (un/)marshalled by the codec -type Queue interface { - // Push() inserts the elements to the rear of the queue - Push(interface{}) - - // Popping/Peeking on an empty queue will cause panic - // The user should check IsEmpty() before doing any actions - - // Peek() returns the element at the front of the queue without removing it - Peek(interface{}) error - - // Pop() returns the element at the front of the queue and removes it - Pop() - - // IsEmpty() checks if the queue is empty - IsEmpty() bool - - // Flush() removes elements it processed - // Return true in the continuation to break - // The interface{} is unmarshalled before the continuation is called - // Starts from the top(head) of the queue - // CONTRACT: Pop() or Push() should not be performed while flushing - Flush(interface{}, func() bool) -} - -// NewQueue constructs new Queue -func NewQueue(cdc *codec.Codec, store sdk.KVStore, keys *LinearKeys) Queue { - return NewLinear(cdc, store, keys) -} - -// Key for the top element position in the queue -func (m Linear) TopKey() []byte { - return m.keys.TopKey -} - -func (m Linear) getTop() (res uint64) { - bz := m.store.Get(m.TopKey()) - if bz == nil { - return 0 - } - - m.cdc.MustUnmarshalBinary(bz, &res) - return -} - -func (m Linear) setTop(top uint64) { - bz := m.cdc.MustMarshalBinary(top) - m.store.Set(m.TopKey(), bz) -} - -// Peek implements Queue -func (m Linear) Peek(ptr interface{}) error { - top := m.getTop() - return m.Get(top, ptr) -} - -// Pop implements Queue -func (m Linear) Pop() { - top := m.getTop() - m.Delete(top) - m.setTop(top + 1) -} - -// IsEmpty implements Queue -func (m Linear) IsEmpty() bool { - top := m.getTop() - length := m.Len() - return top >= length -} - -// Flush implements Queue -func (m Linear) Flush(ptr interface{}, fn func() bool) { - top := m.getTop() - length := m.Len() - - var i uint64 - for i = top; i < length; i++ { - err := m.Get(i, ptr) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - m.Delete(i) - if fn() { - break - } - } - m.setTop(i) -} - -func subspace(prefix []byte) (start, end []byte) { - end = make([]byte, len(prefix)) - copy(end, prefix) - end[len(end)-1]++ - return prefix, end -} From 27b8322a274dcea00fde17f023cfebe96d92df33 Mon Sep 17 00:00:00 2001 From: mossid Date: Tue, 16 Oct 2018 04:09:41 +0900 Subject: [PATCH 2/2] split test --- store/list_test.go | 75 +++++++++++++++++++++++++ store/{linear_test.go => queue_test.go} | 69 ----------------------- 2 files changed, 75 insertions(+), 69 deletions(-) create mode 100644 store/list_test.go rename store/{linear_test.go => queue_test.go} (60%) diff --git a/store/list_test.go b/store/list_test.go new file mode 100644 index 000000000..396e2d1a1 --- /dev/null +++ b/store/list_test.go @@ -0,0 +1,75 @@ +package store + +import ( + "math/rand" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestList(t *testing.T) { + key := sdk.NewKVStoreKey("test") + ctx, cdc := defaultComponents(key) + store := ctx.KVStore(key) + lm := NewList(cdc, store) + + val := S{1, true} + var res S + + lm.Push(val) + require.Equal(t, uint64(1), lm.Len()) + lm.Get(uint64(0), &res) + require.Equal(t, val, res) + + val = S{2, false} + lm.Set(uint64(0), val) + lm.Get(uint64(0), &res) + require.Equal(t, val, res) + + val = S{100, false} + lm.Push(val) + require.Equal(t, uint64(2), lm.Len()) + lm.Get(uint64(1), &res) + require.Equal(t, val, res) + + lm.Delete(uint64(1)) + require.Equal(t, uint64(2), lm.Len()) + + lm.Iterate(&res, func(index uint64) (brk bool) { + var temp S + lm.Get(index, &temp) + require.Equal(t, temp, res) + + require.True(t, index != 1) + return + }) + + lm.Iterate(&res, func(index uint64) (brk bool) { + lm.Set(index, S{res.I + 1, !res.B}) + return + }) + + lm.Get(uint64(0), &res) + require.Equal(t, S{3, true}, res) +} + +func TestListRandom(t *testing.T) { + key := sdk.NewKVStoreKey("test") + ctx, cdc := defaultComponents(key) + store := ctx.KVStore(key) + list := NewList(cdc, store) + mocklist := []uint32{} + + for i := 0; i < 100; i++ { + item := rand.Uint32() + list.Push(item) + mocklist = append(mocklist, item) + } + + for k, v := range mocklist { + var i uint32 + require.NotPanics(t, func() { list.Get(uint64(k), &i) }) + require.Equal(t, v, i) + } +} diff --git a/store/linear_test.go b/store/queue_test.go similarity index 60% rename from store/linear_test.go rename to store/queue_test.go index c8153a041..5ea6d906a 100644 --- a/store/linear_test.go +++ b/store/queue_test.go @@ -1,7 +1,6 @@ package store import ( - // "math/rand" "testing" "github.com/stretchr/testify/require" @@ -30,52 +29,6 @@ func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { return ctx, cdc } -func TestList(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - lm := NewList(cdc, store) - - val := S{1, true} - var res S - - lm.Push(val) - require.Equal(t, uint64(1), lm.Len()) - lm.Get(uint64(0), &res) - require.Equal(t, val, res) - - val = S{2, false} - lm.Set(uint64(0), val) - lm.Get(uint64(0), &res) - require.Equal(t, val, res) - - val = S{100, false} - lm.Push(val) - require.Equal(t, uint64(2), lm.Len()) - lm.Get(uint64(1), &res) - require.Equal(t, val, res) - - lm.Delete(uint64(1)) - require.Equal(t, uint64(2), lm.Len()) - - lm.Iterate(&res, func(index uint64) (brk bool) { - var temp S - lm.Get(index, &temp) - require.Equal(t, temp, res) - - require.True(t, index != 1) - return - }) - - lm.Iterate(&res, func(index uint64) (brk bool) { - lm.Set(index, S{res.I + 1, !res.B}) - return - }) - - lm.Get(uint64(0), &res) - require.Equal(t, S{3, true}, res) -} - func TestQueue(t *testing.T) { key := sdk.NewKVStoreKey("test") ctx, cdc := defaultComponents(key) @@ -146,26 +99,4 @@ func TestKeys(t *testing.T) { err = cdc.UnmarshalBinary(store.Get(TopKey()), &top) require.Nil(t, err) require.Equal(t, top, queue.getTop()) - } - -/* -func TestListRandom(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - list := NewList(cdc, store) - mocklist := []uint32{} - - for i := 0; i < 10000; { - limit := rand.Int31() % 1000 - for j := int32(0); j < limit; j++ { - item := rand.Uint32() - list.Push(item) - mocklist = append(mocklist, item) - } - - require.Equal() - } -} -*/