File: map.go

package info (click to toggle)
hugo 0.153.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 49,384 kB
  • sloc: javascript: 31,879; ansic: 2,321; xml: 350; makefile: 197; asm: 67; sh: 50
file content (117 lines) | stat: -rw-r--r-- 2,830 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2025 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package maps

import (
	"iter"
	"sync"
)

func NewMap[K comparable, T any]() *Map[K, T] {
	return &Map[K, T]{
		m: make(map[K]T),
	}
}

// Map is a thread safe map backed by a Go map.
type Map[K comparable, T any] struct {
	m  map[K]T
	mu sync.RWMutex
}

// Get gets the value for the given key.
// It returns the zero value of T if the key is not found.
func (m *Map[K, T]) Get(key K) T {
	v, _ := m.Lookup(key)
	return v
}

// Lookup looks up the given key in the map.
// It returns the value and a boolean indicating whether the key was found.
func (m *Map[K, T]) Lookup(key K) (T, bool) {
	m.mu.RLock()
	v, found := m.m[key]
	m.mu.RUnlock()
	return v, found
}

// GetOrCreate gets the value for the given key if it exists, or creates it if not.
func (m *Map[K, T]) GetOrCreate(key K, create func() (T, error)) (T, error) {
	v, found := m.Lookup(key)
	if found {
		return v, nil
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	v, found = m.m[key]
	if found {
		return v, nil
	}
	v, err := create()
	if err != nil {
		return v, err
	}
	m.m[key] = v
	return v, nil
}

// Set sets the given key to the given value.
func (m *Map[K, T]) Set(key K, value T) {
	m.mu.Lock()
	m.m[key] = value
	m.mu.Unlock()
}

// WithWriteLock executes the given function with a write lock on the map.
func (m *Map[K, T]) WithWriteLock(f func(m map[K]T) error) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	return f(m.m)
}

// SetIfAbsent sets the given key to the given value if the key does not already exist in the map.
// It returns true if the value was set, false otherwise.
func (m *Map[K, T]) SetIfAbsent(key K, value T) bool {
	m.mu.RLock()
	if _, found := m.m[key]; !found {
		m.mu.RUnlock()
		return m.doSetIfAbsent(key, value)
	}
	m.mu.RUnlock()
	return false
}

func (m *Map[K, T]) doSetIfAbsent(key K, value T) bool {
	m.mu.Lock()
	defer m.mu.Unlock()
	if _, found := m.m[key]; !found {
		m.m[key] = value
		return true
	}
	return false
}

// All returns an iterator over all key/value pairs in the map.
// A read lock is held during the iteration.
func (m *Map[K, T]) All() iter.Seq2[K, T] {
	return func(yield func(K, T) bool) {
		m.mu.RLock()
		defer m.mu.RUnlock()
		for k, v := range m.m {
			if !yield(k, v) {
				return
			}
		}
	}
}