Replace the concurrent map with an identical upstream version

This commit is contained in:
Bjørn Erik Pedersen
2026-04-19 22:14:14 +02:00
parent 927666005c
commit 7574e35b40
6 changed files with 16 additions and 216 deletions
-129
View File
@@ -1,129 +0,0 @@
// Copyright 2026 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 hmaps
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()
}
// Delete deletes the given key from the map.
// It returns true if the key was found and deleted, false otherwise.
func (m *Map[K, T]) Delete(key K) bool {
m.mu.Lock()
defer m.mu.Unlock()
if _, found := m.m[key]; found {
delete(m.m, key)
return true
}
return false
}
// 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
}
}
}
}
-72
View File
@@ -1,72 +0,0 @@
// Copyright 2026 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 hmaps
import (
"testing"
qt "github.com/frankban/quicktest"
)
func TestMap(t *testing.T) {
c := qt.New(t)
m := NewMap[string, int]()
m.Set("b", 42)
v, found := m.Lookup("b")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 42)
v = m.Get("b")
c.Assert(v, qt.Equals, 42)
v, found = m.Lookup("c")
c.Assert(found, qt.Equals, false)
c.Assert(v, qt.Equals, 0)
v = m.Get("c")
c.Assert(v, qt.Equals, 0)
v, err := m.GetOrCreate("d", func() (int, error) {
return 100, nil
})
c.Assert(err, qt.IsNil)
c.Assert(v, qt.Equals, 100)
v, found = m.Lookup("d")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 100)
v, err = m.GetOrCreate("d", func() (int, error) {
return 200, nil
})
c.Assert(err, qt.IsNil)
c.Assert(v, qt.Equals, 100)
wasSet := m.SetIfAbsent("e", 300)
c.Assert(wasSet, qt.Equals, true)
v, found = m.Lookup("e")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 300)
wasSet = m.SetIfAbsent("e", 400)
c.Assert(wasSet, qt.Equals, false)
v, found = m.Lookup("e")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 300)
m.WithWriteLock(func(m map[string]int) error {
m["f"] = 500
return nil
})
v, found = m.Lookup("f")
c.Assert(found, qt.Equals, true)
c.Assert(v, qt.Equals, 500)
}