maps.go

  1package csync
  2
  3import (
  4	"encoding/json"
  5	"iter"
  6	"maps"
  7	"sync"
  8)
  9
 10// Map is a concurrent map implementation that provides thread-safe access.
 11type Map[K comparable, V any] struct {
 12	inner map[K]V
 13	mu    sync.RWMutex
 14}
 15
 16// NewMap creates a new thread-safe map with the specified key and value types.
 17func NewMap[K comparable, V any]() *Map[K, V] {
 18	return &Map[K, V]{
 19		inner: make(map[K]V),
 20	}
 21}
 22
 23// NewMapFrom creates a new thread-safe map from an existing map.
 24func NewMapFrom[K comparable, V any](m map[K]V) *Map[K, V] {
 25	return &Map[K, V]{
 26		inner: m,
 27	}
 28}
 29
 30// NewLazyMap creates a new lazy-loaded map. The provided load function is
 31// executed in a separate goroutine to populate the map.
 32func NewLazyMap[K comparable, V any](load func() map[K]V) *Map[K, V] {
 33	m := &Map[K, V]{}
 34	m.mu.Lock()
 35	go func() {
 36		m.inner = load()
 37		m.mu.Unlock()
 38	}()
 39	return m
 40}
 41
 42// Set sets the value for the specified key in the map.
 43func (m *Map[K, V]) Set(key K, value V) {
 44	m.mu.Lock()
 45	defer m.mu.Unlock()
 46	m.inner[key] = value
 47}
 48
 49// Del deletes the specified key from the map.
 50func (m *Map[K, V]) Del(key K) {
 51	m.mu.Lock()
 52	defer m.mu.Unlock()
 53	delete(m.inner, key)
 54}
 55
 56// Get gets the value for the specified key from the map.
 57func (m *Map[K, V]) Get(key K) (V, bool) {
 58	m.mu.RLock()
 59	defer m.mu.RUnlock()
 60	v, ok := m.inner[key]
 61	return v, ok
 62}
 63
 64// Len returns the number of items in the map.
 65func (m *Map[K, V]) Len() int {
 66	m.mu.RLock()
 67	defer m.mu.RUnlock()
 68	return len(m.inner)
 69}
 70
 71// GetOrSet gets and returns the key if it exists, otherwise, it executes the
 72// given function, set its return value for the given key, and returns it.
 73func (m *Map[K, V]) GetOrSet(key K, fn func() V) V {
 74	got, ok := m.Get(key)
 75	if ok {
 76		return got
 77	}
 78	value := fn()
 79	m.Set(key, value)
 80	return value
 81}
 82
 83// Take gets an item and then deletes it.
 84func (m *Map[K, V]) Take(key K) (V, bool) {
 85	m.mu.Lock()
 86	defer m.mu.Unlock()
 87	v, ok := m.inner[key]
 88	delete(m.inner, key)
 89	return v, ok
 90}
 91
 92// Seq2 returns an iter.Seq2 that yields key-value pairs from the map.
 93func (m *Map[K, V]) Seq2() iter.Seq2[K, V] {
 94	dst := make(map[K]V)
 95	m.mu.RLock()
 96	maps.Copy(dst, m.inner)
 97	m.mu.RUnlock()
 98	return func(yield func(K, V) bool) {
 99		for k, v := range dst {
100			if !yield(k, v) {
101				return
102			}
103		}
104	}
105}
106
107// Seq returns an iter.Seq that yields values from the map.
108func (m *Map[K, V]) Seq() iter.Seq[V] {
109	return func(yield func(V) bool) {
110		for _, v := range m.Seq2() {
111			if !yield(v) {
112				return
113			}
114		}
115	}
116}
117
118var (
119	_ json.Unmarshaler = &Map[string, any]{}
120	_ json.Marshaler   = &Map[string, any]{}
121)
122
123func (Map[K, V]) JSONSchemaAlias() any { //nolint
124	m := map[K]V{}
125	return m
126}
127
128// UnmarshalJSON implements json.Unmarshaler.
129func (m *Map[K, V]) UnmarshalJSON(data []byte) error {
130	m.mu.Lock()
131	defer m.mu.Unlock()
132	m.inner = make(map[K]V)
133	return json.Unmarshal(data, &m.inner)
134}
135
136// MarshalJSON implements json.Marshaler.
137func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
138	m.mu.RLock()
139	defer m.mu.RUnlock()
140	return json.Marshal(m.inner)
141}