Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Malleability] Update mapBackData to use generics and implement BackData #7098

Open
wants to merge 4 commits into
base: andron/BackData-generics-refactoring
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions module/mempool/mutable_back_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type MutableBackData[K comparable, V any] interface {

// Adjust adjusts the value using the given function if the given key can be found.
// Returns a bool which indicates whether the value was updated as well as the updated value.
Adjust(key K, f func(value V) V) (V, bool)
Adjust(key K, f func(value V) (K, V)) (V, bool)

// AdjustWithInit adjusts the value using the given function if the given key can be found. When the
// value is not found, it initializes the value using the given init function and then applies the adjust function.
Expand All @@ -23,5 +23,5 @@ type MutableBackData[K comparable, V any] interface {
// - the adjusted value.
//
// - a bool which indicates whether the value was adjusted.
AdjustWithInit(key K, adjust func(value V) V, init func() V) (V, bool)
AdjustWithInit(key K, adjust func(value V) (K, V), init func() V) (V, bool)
}
170 changes: 86 additions & 84 deletions module/mempool/stdmap/backdata/mapBackData.go
Original file line number Diff line number Diff line change
@@ -1,145 +1,147 @@
package backdata

import (
"github.com/onflow/flow-go/model/flow"
)

// MapBackData implements a map-based generic memory BackData backed by a Go map.
// Note that this implementation is NOT thread-safe, and the higher-level Backend is responsible for concurrency management.
type MapBackData struct {
type MapBackData[K comparable, V any] struct {
// NOTE: as a BackData implementation, MapBackData must be non-blocking.
// Concurrency management is done by overlay Backend.
entities map[flow.Identifier]flow.Entity
dataMap map[K]V
}

func NewMapBackData() *MapBackData {
bd := &MapBackData{
entities: make(map[flow.Identifier]flow.Entity),
func NewMapBackData[K comparable, V any]() *MapBackData[K, V] {
bd := &MapBackData[K, V]{
dataMap: make(map[K]V),
}
return bd
}

// Has checks if backdata already contains the entity with the given identifier.
func (b *MapBackData) Has(entityID flow.Identifier) bool {
_, exists := b.entities[entityID]
// Has checks if backdata already contains the value with the given key.
func (b *MapBackData[K, V]) Has(key K) bool {
_, exists := b.dataMap[key]
return exists
}

// Add adds the given entity to the backdata.
func (b *MapBackData) Add(entityID flow.Identifier, entity flow.Entity) bool {
_, exists := b.entities[entityID]
// Add adds the given value to the backdata.
func (b *MapBackData[K, V]) Add(key K, value V) bool {
_, exists := b.dataMap[key]
if exists {
return false
}
b.entities[entityID] = entity
b.dataMap[key] = value
return true
}

// Remove removes the entity with the given identifier.
func (b *MapBackData) Remove(entityID flow.Identifier) (flow.Entity, bool) {
entity, exists := b.entities[entityID]
// Remove removes the value with the given key.
func (b *MapBackData[K, V]) Remove(key K) (V, bool) {
value, exists := b.dataMap[key]
if !exists {
return nil, false
var zero V
return zero, false
}
delete(b.entities, entityID)
return entity, true
delete(b.dataMap, key)
return value, true
}

// Adjust adjusts the entity using the given function if the given identifier can be found.
// Returns a bool which indicates whether the entity was updated as well as the updated entity.
func (b *MapBackData) Adjust(entityID flow.Identifier, f func(flow.Entity) flow.Entity) (flow.Entity, bool) {
entity, ok := b.entities[entityID]
// Adjust adjusts the value using the given function if the given key can be found.
// It returns the updated value along with a boolean indicating whether an update occurred.
func (b *MapBackData[K, V]) Adjust(key K, f func(V) (K, V)) (V, bool) {
value, ok := b.dataMap[key]
if !ok {
return nil, false
var zero V
return zero, false
}
newentity := f(entity)
newentityID := newentity.ID()
newKey, newValue := f(value)

delete(b.entities, entityID)
b.entities[newentityID] = newentity
return newentity, true
delete(b.dataMap, key)
b.dataMap[newKey] = newValue
return newValue, true
}

// AdjustWithInit adjusts the entity using the given function if the given identifier can be found. When the
// entity is not found, it initializes the entity using the given init function and then applies the adjust function.
// AdjustWithInit adjusts the value using the provided function if the key is found.
// If the key is not found, it initializes the value using the given init function and then applies the adjustment.
//
// Args:
// - entityID: the identifier of the entity to adjust.
// - adjust: the function that adjusts the entity.
// - init: the function that initializes the entity when it is not found.
// - key: The key for which the value should be adjusted.
// - adjust: the function that adjusts the value.
// - init: A function that initializes the value if the key is not present.
//
// Returns:
// - the adjusted entity.
// - the adjusted value.
//
// - a bool which indicates whether the entity was adjusted.
func (b *MapBackData) AdjustWithInit(entityID flow.Identifier, adjust func(flow.Entity) flow.Entity, init func() flow.Entity) (flow.Entity, bool) {
if b.Has(entityID) {
return b.Adjust(entityID, adjust)
// - a bool which indicates whether the value was adjusted.
func (b *MapBackData[K, V]) AdjustWithInit(key K, adjust func(V) (K, V), init func() V) (V, bool) {
if b.Has(key) {
return b.Adjust(key, adjust)
}
b.Add(entityID, init())
return b.Adjust(entityID, adjust)
b.Add(key, init())
return b.Adjust(key, adjust)
}

// GetWithInit returns the given entity from the backdata. If the entity does not exist, it creates a new entity
// using the factory function and stores it in the backdata.
// GetWithInit returns the value for the given key.
// If the key does not exist, it creates a new value using the init function, stores it, and returns it.
//
// Args:
// - entityID: the identifier of the entity to get.
// - init: the function that initializes the entity when it is not found.
// - key: The key for which the value should be retrieved.
// - init: A function that initializes the value if the key is not present.
//
// Returns:
// - the entity.
// - a bool which indicates whether the entity was found (or created).
func (b *MapBackData) GetWithInit(entityID flow.Identifier, init func() flow.Entity) (flow.Entity, bool) {
if b.Has(entityID) {
return b.ByID(entityID)
// - the value.
// - a bool which indicates whether the value was found (or created).
func (b *MapBackData[K, V]) GetWithInit(key K, init func() V) (V, bool) {
if b.Has(key) {
return b.ByID(key)
}
b.Add(entityID, init())
return b.ByID(entityID)
b.Add(key, init())
return b.ByID(key)
}

// ByID returns the given entity from the backdata.
func (b *MapBackData) ByID(entityID flow.Identifier) (flow.Entity, bool) {
entity, exists := b.entities[entityID]
// ByID returns the value for the given key.
func (b *MapBackData[K, V]) ByID(key K) (V, bool) {
value, exists := b.dataMap[key]
if !exists {
return nil, false
var zero V
return zero, false
}
return entity, true
return value, true
}

// Size returns the size of the backdata, i.e., total number of stored (entityId, entity)
func (b *MapBackData) Size() uint {
return uint(len(b.entities))
// Size returns the number of stored key-value pairs.
func (b *MapBackData[K, V]) Size() uint {
return uint(len(b.dataMap))
}

// All returns all entities stored in the backdata.
func (b *MapBackData) All() map[flow.Identifier]flow.Entity {
entities := make(map[flow.Identifier]flow.Entity)
for entityID, entity := range b.entities {
entities[entityID] = entity
// All returns all stored key-value pairs as a map.
func (b *MapBackData[K, V]) All() map[K]V {
values := make(map[K]V)
for key, value := range b.dataMap {
values[key] = value
}
return entities
return values
}

// Identifiers returns the list of identifiers of entities stored in the backdata.
func (b *MapBackData) Identifiers() flow.IdentifierList {
ids := make(flow.IdentifierList, len(b.entities))
// Identifiers returns the list of keys of values stored in the backdata.
func (b *MapBackData[K, V]) Identifiers() []K {
keys := make([]K, len(b.dataMap))
i := 0
for entityID := range b.entities {
ids[i] = entityID
for key := range b.dataMap {
keys[i] = key
i++
}
return ids
return keys
}

// Entities returns the list of entities stored in the backdata.
func (b *MapBackData) Entities() []flow.Entity {
entities := make([]flow.Entity, len(b.entities))
// Entities returns the list of values stored in the backdata.
func (b *MapBackData[K, V]) Entities() []V {
values := make([]V, len(b.dataMap))
i := 0
for _, entity := range b.entities {
entities[i] = entity
for _, value := range b.dataMap {
values[i] = value
i++
}
return entities
return values
}

// Clear removes all entities from the backdata.
func (b *MapBackData) Clear() {
b.entities = make(map[flow.Identifier]flow.Entity)
// Clear removes all values from the backdata.
func (b *MapBackData[K, V]) Clear() {
b.dataMap = make(map[K]V)
}
Loading