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) }