commit 6f48f8af8fe5db33a51ddec72cd78ba417c665ce Author: Milarin Date: Thu Feb 15 15:57:41 2024 +0100 initial commit diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c85656b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.milar.in/milarin/cmap + +go 1.22.0 diff --git a/map.go b/map.go new file mode 100644 index 0000000..941dcbf --- /dev/null +++ b/map.go @@ -0,0 +1,119 @@ +package cmap + +import "sync" + +// Map represents a map safe for concurrent use +type Map[K comparable, V any] struct { + mutex sync.RWMutex + data map[K]V +} + +// New creates a new generic Map +func New[K comparable, V any]() *Map[K, V] { + return &Map[K, V]{data: map[K]V{}} +} + +// Put puts the key-value pair into m +func (m *Map[K, V]) Put(key K, value V) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.data[key] = value +} + +// GetHas returns the corresponding value for the given key in m +// as well as a bool indicating if a value was present at all +func (m *Map[K, V]) GetHas(key K) (V, bool) { + m.mutex.RLock() + defer m.mutex.RUnlock() + value, ok := m.data[key] + return value, ok +} + +// Get returns the corresponding value for the given key in m +func (m *Map[K, V]) Get(key K) V { + value, _ := m.GetHas(key) + return value +} + +// Has returns a bool indicating if a value was present for the given key +func (m *Map[K, V]) Has(key K) bool { + _, ok := m.GetHas(key) + return ok +} + +// Delete deletes the key-value pair from m +func (m *Map[K, V]) Delete(key K) { + m.mutex.Lock() + defer m.mutex.Unlock() + delete(m.data, key) +} + +// Count returns the amount of key-value pairs currently stored in m +func (m *Map[K, V]) Count() int { + m.mutex.RLock() + defer m.mutex.RUnlock() + return len(m.data) +} + +// Iter calls f for every key-value pair stored in m. +// Be aware that this locks m for write access +// as pointer types could be modified in f +func (m *Map[K, V]) Iter(f func(key K, value V)) { + m.mutex.Lock() + defer m.mutex.Unlock() + + for key, value := range m.data { + f(key, value) + } +} + +// Keys returns a slice containing all currently stored keys in m. +// Pointer values are returned as they were received +func (m *Map[K, V]) Keys() []K { + m.mutex.RLock() + defer m.mutex.RUnlock() + + keys := make([]K, 0, len(m.data)) + for key := range m.data { + keys = append(keys, key) + } + return keys +} + +// Values returns a slice containing all currently stored values in m. +// Pointer values are returned as they were received +func (m *Map[K, V]) Values() []V { + m.mutex.RLock() + defer m.mutex.RUnlock() + + values := make([]V, 0, len(m.data)) + for _, value := range m.data { + values = append(values, value) + } + return values +} + +// Do calls f with the underlying primitive map. +// Be aware that this locks m for write access +// so Do can be used for reading as well as modifiying m. +// After f returns, the map will be shallow-copied in order to prevent +// clueless programmers from storing the map and modifying it afterwards. +// If you need that little bit of optimization, use DoUnsafe instead +func (m *Map[K, V]) Do(f func(m map[K]V)) { + m.mutex.Lock() + defer m.mutex.Unlock() + f(m.data) + + nm := map[K]V{} + for key, value := range m.data { + nm[key] = value + } + m.data = nm +} + +// DoUnsafe behaves like Do but does not create a shallow-copy of m +func (m *Map[K, V]) DoUnsafe(f func(m map[K]V)) { + m.mutex.Lock() + defer m.mutex.Unlock() + f(m.data) +}