Skip to content
Snippets Groups Projects
Unverified Commit 6518b46d authored by Jonas Theis's avatar Jonas Theis Committed by GitHub
Browse files

Move datastructures to hive.go (#724)

* Update to latest hive.go changes

* Move datastructures from GoShimmer to hive.go
parent 14592f27
Branches
Tags
No related merge requests found
Showing
with 12 additions and 679 deletions
......@@ -14,9 +14,9 @@ import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
"github.com/iotaledger/goshimmer/plugins/issuer"
"github.com/iotaledger/hive.go/datastructure/orderedmap"
)
var (
......
......@@ -2,21 +2,21 @@ package tipmanager
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/packages/binary/datastructure"
"github.com/iotaledger/hive.go/datastructure/randommap"
"github.com/iotaledger/hive.go/events"
)
// TipManager manages liked tips and emits events for their removal and addition.
type TipManager struct {
// tips are all currently liked tips.
tips *datastructure.RandomMap
tips *randommap.RandomMap
Events Events
}
// New creates a new TipManager.
func New() *TipManager {
return &TipManager{
tips: datastructure.NewRandomMap(),
tips: randommap.New(),
Events: Events{
TipAdded: events.NewEvent(payloadIDEvent),
TipRemoved: events.NewEvent(payloadIDEvent),
......
......@@ -2,7 +2,7 @@ package transaction
import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
"github.com/iotaledger/hive.go/datastructure/orderedmap"
"github.com/iotaledger/hive.go/marshalutil"
)
......
......@@ -6,7 +6,7 @@ import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
"github.com/iotaledger/hive.go/datastructure/orderedmap"
"github.com/iotaledger/hive.go/marshalutil"
)
......
package transaction
import (
"github.com/iotaledger/hive.go/datastructure/orderedmap"
"github.com/iotaledger/hive.go/marshalutil"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
)
// Signatures represents a container for the address signatures of a value transfer.
......
package orderedmap
// Element defines the model of each element of the orderedMap.
type Element struct {
key interface{}
value interface{}
prev *Element
next *Element
}
package orderedmap
import (
"sync"
)
// OrderedMap provides a concurrent-safe ordered map.
type OrderedMap struct {
head *Element
tail *Element
dictionary map[interface{}]*Element
size int
mutex sync.RWMutex
}
// New returns a new *OrderedMap.
func New() *OrderedMap {
return &OrderedMap{
dictionary: make(map[interface{}]*Element),
}
}
// Get returns the value mapped to the given key if exists.
func (orderedMap *OrderedMap) Get(key interface{}) (interface{}, bool) {
orderedMap.mutex.RLock()
defer orderedMap.mutex.RUnlock()
orderedMapElement, orderedMapElementExists := orderedMap.dictionary[key]
if !orderedMapElementExists {
return nil, false
}
return orderedMapElement.value, true
}
// Set adds a key-value pair to the orderedMap. It returns false if the same pair already exists.
func (orderedMap *OrderedMap) Set(key interface{}, newValue interface{}) bool {
if oldValue, oldValueExists := orderedMap.Get(key); oldValueExists && oldValue == newValue {
return false
}
orderedMap.mutex.Lock()
defer orderedMap.mutex.Unlock()
if oldValue, oldValueExists := orderedMap.dictionary[key]; oldValueExists {
if oldValue.value == newValue {
return false
}
oldValue.value = newValue
return true
}
newElement := &Element{
key: key,
value: newValue,
}
if orderedMap.head == nil {
orderedMap.head = newElement
} else {
orderedMap.tail.next = newElement
newElement.prev = orderedMap.tail
}
orderedMap.tail = newElement
orderedMap.size++
orderedMap.dictionary[key] = newElement
return true
}
// ForEach iterates through the orderedMap and calls the consumer function for every element.
// The iteration can be aborted by returning false in the consumer.
func (orderedMap *OrderedMap) ForEach(consumer func(key, value interface{}) bool) bool {
orderedMap.mutex.RLock()
currentEntry := orderedMap.head
orderedMap.mutex.RUnlock()
for currentEntry != nil {
if !consumer(currentEntry.key, currentEntry.value) {
return false
}
orderedMap.mutex.RLock()
currentEntry = currentEntry.next
orderedMap.mutex.RUnlock()
}
return true
}
// Delete deletes the given key (and related value) from the orderedMap.
// It returns false if the key is not found.
func (orderedMap *OrderedMap) Delete(key interface{}) bool {
if _, valueExists := orderedMap.Get(key); !valueExists {
return false
}
orderedMap.mutex.Lock()
defer orderedMap.mutex.Unlock()
value, valueExists := orderedMap.dictionary[key]
if !valueExists {
return false
}
delete(orderedMap.dictionary, key)
orderedMap.size--
if value.prev != nil {
value.prev.next = value.next
} else {
orderedMap.head = value.next
}
if value.next != nil {
value.next.prev = value.prev
} else {
orderedMap.tail = value.prev
}
return true
}
// Size returns the size of the orderedMap.
func (orderedMap *OrderedMap) Size() int {
orderedMap.mutex.RLock()
defer orderedMap.mutex.RUnlock()
return orderedMap.size
}
package orderedmap
import (
"fmt"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestOrderedMap_Size(t *testing.T) {
orderedMap := New()
assert.Equal(t, 0, orderedMap.Size())
orderedMap.Set(1, 1)
assert.Equal(t, 1, orderedMap.Size())
orderedMap.Set(3, 1)
orderedMap.Set(2, 1)
assert.Equal(t, 3, orderedMap.Size())
orderedMap.Set(2, 2)
assert.Equal(t, 3, orderedMap.Size())
orderedMap.Delete(2)
assert.Equal(t, 2, orderedMap.Size())
}
func TestNew(t *testing.T) {
orderedMap := New()
require.NotNil(t, orderedMap)
assert.Equal(t, 0, orderedMap.Size())
assert.Nil(t, orderedMap.head)
assert.Nil(t, orderedMap.tail)
}
func TestSetGetDelete(t *testing.T) {
orderedMap := New()
require.NotNil(t, orderedMap)
// when adding the first new key,value pair, we must return true
keyValueAdded := orderedMap.Set("key", "value")
assert.True(t, keyValueAdded)
// we should be able to retrieve the just added element
value, ok := orderedMap.Get("key")
assert.Equal(t, "value", value)
assert.True(t, ok)
// head and tail should NOT be nil and match and size should be 1
assert.NotNil(t, orderedMap.head)
assert.Same(t, orderedMap.head, orderedMap.tail)
assert.Equal(t, 1, orderedMap.Size())
// when adding the same key,value pair must return false
// and size should not change;
keyValueAdded = orderedMap.Set("key", "value")
assert.False(t, keyValueAdded)
assert.Equal(t, 1, orderedMap.Size())
// when retrieving something that does not exist we
// should get nil, false
value, ok = orderedMap.Get("keyNotStored")
assert.Nil(t, value)
assert.False(t, ok)
// when deleting an existing element, we must get true,
// the element must be removed, and size decremented.
deleted := orderedMap.Delete("key")
assert.True(t, deleted)
value, ok = orderedMap.Get("key")
assert.Nil(t, value)
assert.False(t, ok)
assert.Equal(t, 0, orderedMap.Size())
// if we delete the only element, head and tail should be both nil
assert.Nil(t, orderedMap.head)
assert.Same(t, orderedMap.head, orderedMap.tail)
// when deleting a NON existing element, we must get false
deleted = orderedMap.Delete("key")
assert.False(t, deleted)
}
func TestForEach(t *testing.T) {
orderedMap := New()
require.NotNil(t, orderedMap)
testElements := []Element{
{key: "one", value: 1},
{key: "two", value: 2},
{key: "three", value: 3},
}
for _, element := range testElements {
keyValueAdded := orderedMap.Set(element.key, element.value)
assert.True(t, keyValueAdded)
}
// test that all elements are positive via ForEach
testPositive := orderedMap.ForEach(func(key, value interface{}) bool {
return value.(int) > 0
})
assert.True(t, testPositive)
testNegative := orderedMap.ForEach(func(key, value interface{}) bool {
return value.(int) < 0
})
assert.False(t, testNegative)
}
func TestConcurrencySafe(t *testing.T) {
orderedMap := New()
require.NotNil(t, orderedMap)
// initialize a slice of 100 elements
set := make([]Element, 100)
for i := 0; i < 100; i++ {
element := Element{key: fmt.Sprintf("%d", i), value: i}
set[i] = element
}
// let 10 workers fill the orderedMap
workers := 10
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
ele := set[i]
orderedMap.Set(ele.key, ele.value)
}
}()
}
wg.Wait()
// check that all the elements consumed from the set
// have been stored in the orderedMap and its size matches
for i := 0; i < 100; i++ {
value, ok := orderedMap.Get(set[i].key)
assert.Equal(t, set[i].value, value)
assert.True(t, ok)
}
assert.Equal(t, 100, orderedMap.Size())
// let 10 workers delete elements from the orderedMAp
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
ele := set[i]
orderedMap.Delete(ele.key)
}
}()
}
wg.Wait()
assert.Equal(t, 0, orderedMap.Size())
}
package queue
import (
"sync"
)
// Queue represents a ring buffer.
type Queue struct {
ringBuffer []interface{}
read int
write int
capacity int
size int
mutex sync.Mutex
}
// New creates a new queue with the specified capacity.
func New(capacity int) *Queue {
return &Queue{
ringBuffer: make([]interface{}, capacity),
capacity: capacity,
}
}
// Size returns the size of the queue.
func (queue *Queue) Size() int {
queue.mutex.Lock()
defer queue.mutex.Unlock()
return queue.size
}
// Capacity returns the capacity of the queue.
func (queue *Queue) Capacity() int {
queue.mutex.Lock()
defer queue.mutex.Unlock()
return queue.capacity
}
// Offer adds an element to the queue and returns true.
// If the queue is full, it drops it and returns false.
func (queue *Queue) Offer(element interface{}) bool {
queue.mutex.Lock()
defer queue.mutex.Unlock()
if queue.size == queue.capacity {
return false
}
queue.ringBuffer[queue.write] = element
queue.write = (queue.write + 1) % queue.capacity
queue.size++
return true
}
// Poll returns and removes the oldest element in the queue and true if successful.
// If returns false if the queue is empty.
func (queue *Queue) Poll() (element interface{}, success bool) {
queue.mutex.Lock()
defer queue.mutex.Unlock()
if success = queue.size != 0; !success {
return
}
element = queue.ringBuffer[queue.read]
queue.ringBuffer[queue.read] = nil
queue.read = (queue.read + 1) % queue.capacity
queue.size--
return
}
package queue
import (
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewQueue(t *testing.T) {
queue := New(2)
require.NotNil(t, queue)
assert.Equal(t, 0, queue.Size())
assert.Equal(t, 2, queue.Capacity())
}
func TestQueueOfferPoll(t *testing.T) {
queue := New(2)
require.NotNil(t, queue)
// offer element to queue
assert.True(t, queue.Offer(1))
assert.Equal(t, 1, queue.Size())
assert.True(t, queue.Offer(2))
assert.Equal(t, 2, queue.Size())
assert.False(t, queue.Offer(3))
// Poll element from queue
polledValue, ok := queue.Poll()
assert.True(t, ok)
assert.Equal(t, 1, polledValue)
assert.Equal(t, 1, queue.Size())
polledValue, ok = queue.Poll()
assert.True(t, ok)
assert.Equal(t, 2, polledValue)
assert.Equal(t, 0, queue.Size())
polledValue, ok = queue.Poll()
assert.False(t, ok)
assert.Nil(t, polledValue)
assert.Equal(t, 0, queue.Size())
// Offer the empty queue again
assert.True(t, queue.Offer(3))
assert.Equal(t, 1, queue.Size())
}
func TestQueueOfferConcurrencySafe(t *testing.T) {
queue := New(100)
require.NotNil(t, queue)
// let 10 workers fill the queue
workers := 10
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
queue.Offer(j)
}
}()
}
wg.Wait()
// check that all the elements are offered
assert.Equal(t, 100, queue.Size())
counter := make([]int, 10)
for i := 0; i < 100; i++ {
value, ok := queue.Poll()
assert.True(t, ok)
counter[value.(int)]++
}
assert.Equal(t, 0, queue.Size())
// check that the insert numbers are correct
for i := 0; i < 10; i++ {
assert.Equal(t, 10, counter[i])
}
}
func TestQueuePollConcurrencySafe(t *testing.T) {
queue := New(100)
require.NotNil(t, queue)
for j := 0; j < 100; j++ {
queue.Offer(j)
}
// let 10 workers poll the queue
workers := 10
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
_, ok := queue.Poll()
assert.True(t, ok)
}
}()
}
wg.Wait()
// check that all the elements are polled
assert.Equal(t, 0, queue.Size())
}
package datastructure
import (
"math/rand"
"sync"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type randomMapEntry struct {
key interface{}
value interface{}
keyIndex int
}
// RandomMap defines a map with extended ability to return a random entry.
type RandomMap struct {
rawMap map[interface{}]*randomMapEntry
keys []interface{}
size int
mutex sync.RWMutex
}
// NewRandomMap creates a new random map
func NewRandomMap() *RandomMap {
return &RandomMap{
rawMap: make(map[interface{}]*randomMapEntry),
keys: make([]interface{}, 0),
}
}
// Set associates the specified value with the specified key.
// If the association already exists, it updates the value.
func (rmap *RandomMap) Set(key interface{}, value interface{}) (updated bool) {
rmap.mutex.Lock()
if entry, exists := rmap.rawMap[key]; exists {
if entry.value != value {
entry.value = value
updated = true
}
} else {
rmap.rawMap[key] = &randomMapEntry{
key: key,
value: value,
keyIndex: rmap.size,
}
updated = true
rmap.keys = append(rmap.keys, key)
rmap.size++
}
rmap.mutex.Unlock()
return
}
// Get returns the value to which the specified key is mapped.
func (rmap *RandomMap) Get(key interface{}) (result interface{}, exists bool) {
rmap.mutex.RLock()
if entry, entryExists := rmap.rawMap[key]; entryExists {
result = entry.value
exists = entryExists
}
rmap.mutex.RUnlock()
return
}
// Delete removes the mapping for the specified key in the map.
func (rmap *RandomMap) Delete(key interface{}) (result interface{}, exists bool) {
rmap.mutex.RLock()
if _, entryExists := rmap.rawMap[key]; entryExists {
rmap.mutex.RUnlock()
rmap.mutex.Lock()
if entry, entryExists := rmap.rawMap[key]; entryExists {
delete(rmap.rawMap, key)
rmap.size--
if entry.keyIndex != rmap.size {
oldKey := entry.keyIndex
movedKey := rmap.keys[rmap.size]
rmap.rawMap[movedKey].keyIndex = oldKey
rmap.keys[oldKey] = movedKey
}
rmap.keys = rmap.keys[:rmap.size]
result = entry.value
exists = true
}
rmap.mutex.Unlock()
} else {
rmap.mutex.RUnlock()
}
return
}
// Size returns the number of key-value mappings in the map.
func (rmap *RandomMap) Size() (result int) {
rmap.mutex.RLock()
result = rmap.size
rmap.mutex.RUnlock()
return
}
// ForEach iterates through the elements in the map and calls the consumer function for each element.
func (rmap *RandomMap) ForEach(consumer func(key interface{}, value interface{})) {
rmap.mutex.RLock()
defer rmap.mutex.RUnlock()
for _, key := range rmap.keys {
consumer(key, rmap.rawMap[key].value)
}
}
// RandomKey returns a random key from the map.
func (rmap *RandomMap) RandomKey() (result interface{}) {
rmap.mutex.RLock()
defer rmap.mutex.RUnlock()
return rmap.randomKey()
}
// RandomEntry returns a random value from the map.
func (rmap *RandomMap) RandomEntry() (result interface{}) {
rmap.mutex.RLock()
if rmap.size >= 1 {
result = rmap.rawMap[rmap.randomKey()].value
}
rmap.mutex.RUnlock()
return
}
// Keys returns the list of keys stored in the RandomMap.
func (rmap *RandomMap) Keys() (result []interface{}) {
rmap.mutex.RLock()
defer rmap.mutex.RUnlock()
result = make([]interface{}, rmap.size)
copy(result, rmap.keys)
return
}
func (rmap *RandomMap) randomKey() (result interface{}) {
return rmap.keys[rand.Intn(rmap.size)]
}
......@@ -8,11 +8,11 @@ import (
"testing"
"time"
"github.com/iotaledger/goshimmer/packages/binary/datastructure"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
"github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/iotaledger/hive.go/datastructure/randommap"
"github.com/iotaledger/hive.go/events"
"github.com/iotaledger/hive.go/identity"
"github.com/iotaledger/hive.go/kvstore/mapdb"
......@@ -107,7 +107,7 @@ func TestTangle_MissingMessages(t *testing.T) {
require.NoError(t, err)
// map to keep track of the tips
tips := datastructure.NewRandomMap()
tips := randommap.New()
tips.Set(message.EmptyID, message.EmptyID)
// setup the message factory
......
package tipselector
import (
"github.com/iotaledger/goshimmer/packages/binary/datastructure"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
"github.com/iotaledger/hive.go/datastructure/randommap"
)
// TipSelector manages a map of tips and emits events for their removal and addition.
type TipSelector struct {
tips *datastructure.RandomMap
tips *randommap.RandomMap
Events *Events
}
// New creates a new tip-selector.
func New(tips ...message.ID) *TipSelector {
tipSelector := &TipSelector{
tips: datastructure.NewRandomMap(),
tips: randommap.New(),
Events: newEvents(),
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment