diff --git a/mapi.go b/mapi.go index 31e6bf3..70ed6cd 100644 --- a/mapi.go +++ b/mapi.go @@ -13,7 +13,7 @@ type MapI[K comparable, V any] interface { Has(k K) bool Keys() []K Values() []V - Merge(MapI[K, V]) + Copy(MapI[K, V]) Equal(MapI[K, V]) bool Delete(k K) V All() iter.Seq2[K, V] @@ -22,6 +22,9 @@ type MapI[K comparable, V any] interface { Insert(seq iter.Seq2[K, V]) DeleteFunc(del func(K, V) bool) String() string + + // Deprecated: Call Copy instead + Merge(MapI[K, V]) } // Setter sets a value in a map. diff --git a/mapi_test.go b/mapi_test.go index 33a5c44..721927e 100644 --- a/mapi_test.go +++ b/mapi_test.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/gob" "encoding/json" - "github.com/stretchr/testify/assert" "iter" "slices" "strconv" "testing" + + "github.com/stretchr/testify/assert" ) type makeF func(sources ...mapT) MapI[string, int] @@ -18,7 +19,7 @@ func makeMapi[M any](sources ...mapT) MapI[string, int] { m = new(M) i := m.(MapI[string, int]) for _, s := range sources { - i.Merge(s) + i.Copy(s) } return i } @@ -26,7 +27,7 @@ func makeMapi[M any](sources ...mapT) MapI[string, int] { func runMapiTests[M any](t *testing.T, f makeF) { testClear(t, f) testLen(t, f) - testMerge(t, f) + testCopy(t, f) testGetHasLoad(t, f) testRange(t, f) testSet(t, f) @@ -69,7 +70,7 @@ func testLen(t *testing.T, f makeF) { assert.Equal(t, 2, f(mapT{"a": 1, "b": 2}).Len()) } -func testMerge(t *testing.T, f makeF) { +func testCopy(t *testing.T, f makeF) { tests := []struct { name string m1 mapTI @@ -84,8 +85,8 @@ func testMerge(t *testing.T, f makeF) { {"from cast map", f(mapT{"a": 1}), Cast(map[string]int{"b": 2}), mapT{"a": 1, "b": 2}}, } for _, tt := range tests { - t.Run("Merge "+tt.name, func(t *testing.T) { - tt.m1.Merge(tt.m2) + t.Run("Copy "+tt.name, func(t *testing.T) { + tt.m1.Copy(tt.m2) if !tt.m1.Equal(tt.expected) { t.Errorf("Merge error. Expected: %q, got %q", tt.expected, tt.m1) } diff --git a/safe_map.go b/safe_map.go index 96ada25..b391ee2 100644 --- a/safe_map.go +++ b/safe_map.go @@ -19,7 +19,7 @@ import ( // // Do not make a copy of a SafeMap using the equality operator (=). Use Clone instead. type SafeMap[K comparable, V any] struct { - sync.RWMutex + mu sync.RWMutex items StdMap[K, V] } @@ -38,20 +38,20 @@ func (m *SafeMap[K, V]) Clear() { if m.items == nil { return } - m.Lock() + m.mu.Lock() m.items = nil - m.Unlock() + m.mu.Unlock() } // Set sets the key to the given value. func (m *SafeMap[K, V]) Set(k K, v V) { - m.Lock() + m.mu.Lock() if m.items == nil { m.items = map[K]V{k: v} } else { m.items[k] = v } - m.Unlock() + m.mu.Unlock() } // Get returns the value based on its key. If it does not exist, an empty string will be returned. @@ -72,19 +72,19 @@ func (m *SafeMap[K, V]) Load(k K) (v V, ok bool) { if m.items == nil { return } - m.RLock() + m.mu.RLock() if m.items != nil { v, ok = m.items[k] } - m.RUnlock() + m.mu.RUnlock() return } // Delete removes the key from the map and returns the value. If the key does not exist, the zero value will be returned. func (m *SafeMap[K, V]) Delete(k K) (v V) { - m.Lock() + m.mu.Lock() v = m.items.Delete(k) - m.Unlock() + m.mu.Unlock() return } @@ -94,9 +94,9 @@ func (m *SafeMap[K, V]) Values() (v []V) { if m.items == nil { return } - m.RLock() + m.mu.RLock() v = m.items.Values() - m.RUnlock() + m.mu.RUnlock() return } @@ -106,9 +106,9 @@ func (m *SafeMap[K, V]) Keys() (keys []K) { if m.items == nil { return nil } - m.RLock() + m.mu.RLock() keys = m.items.Keys() - m.RUnlock() + m.mu.RUnlock() return } @@ -117,9 +117,9 @@ func (m *SafeMap[K, V]) Len() (l int) { if m.items == nil { return } - m.RLock() + m.mu.RLock() l = m.items.Len() - m.RUnlock() + m.mu.RUnlock() return } @@ -131,8 +131,8 @@ func (m *SafeMap[K, V]) Range(f func(k K, v V) bool) { if m == nil || m.items == nil { return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m.items.Range(f) } @@ -147,52 +147,52 @@ func (m *SafeMap[K, V]) Copy(in MapI[K, V]) { if m.items == nil { m.items = make(map[K]V, in.Len()) } - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.items.Copy(in) } // Equal returns true if all the keys in the given map exist in this map, and the values are the same func (m *SafeMap[K, V]) Equal(m2 MapI[K, V]) bool { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.items.Equal(m2) } // MarshalBinary implements the BinaryMarshaler interface to convert the map to a byte stream. func (m *SafeMap[K, V]) MarshalBinary() ([]byte, error) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.items.MarshalBinary() } // UnmarshalBinary implements the BinaryUnmarshaler interface to convert a byte stream to a // SafeMap. func (m *SafeMap[K, V]) UnmarshalBinary(data []byte) (err error) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() return m.items.UnmarshalBinary(data) } // MarshalJSON implements the json.Marshaler interface to convert the map into a JSON object. func (m *SafeMap[K, V]) MarshalJSON() (out []byte, err error) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.items.MarshalJSON() } // UnmarshalJSON implements the json.Unmarshaler interface to convert a json object to a SafeMap. // The JSON must start with an object. func (m *SafeMap[K, V]) UnmarshalJSON(in []byte) (err error) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() return m.items.UnmarshalJSON(in) } // String outputs the map as a string. func (m *SafeMap[K, V]) String() string { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.items.String() } @@ -213,8 +213,8 @@ func (m *SafeMap[K, V]) KeysIter() iter.Seq[K] { if m.items == nil { return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() for k := range m.items { if !yield(k) { break @@ -231,8 +231,8 @@ func (m *SafeMap[K, V]) ValuesIter() iter.Seq[V] { if m.items == nil { return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() for _, v := range m.items { if !yield(v) { break @@ -244,8 +244,8 @@ func (m *SafeMap[K, V]) ValuesIter() iter.Seq[V] { // Insert adds the values from seq to the map. // Duplicate keys are overridden. func (m *SafeMap[K, V]) Insert(seq iter.Seq2[K, V]) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() for k, v := range seq { m.items[k] = v } @@ -266,15 +266,15 @@ func CollectSafeMap[K comparable, V any](seq iter.Seq2[K, V]) *SafeMap[K, V] { // the new keys and values are set using ordinary assignment. func (m *SafeMap[K, V]) Clone() *SafeMap[K, V] { m1 := new(SafeMap[K, V]) - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m1.items = m.items.Clone() return m1 } // DeleteFunc deletes any key/value pairs for which del returns true. func (m *SafeMap[K, V]) DeleteFunc(del func(K, V) bool) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.items.DeleteFunc(del) } diff --git a/safe_slice_map.go b/safe_slice_map.go index b306e5a..a9990cd 100644 --- a/safe_slice_map.go +++ b/safe_slice_map.go @@ -28,7 +28,7 @@ import ( // // Do not make a copy of a SafeSliceMap using the equality operator. Use Clone() instead. type SafeSliceMap[K comparable, V any] struct { - sync.RWMutex + mu sync.RWMutex sm SliceMap[K, V] } @@ -47,8 +47,8 @@ func NewSafeSliceMap[K comparable, V any](sources ...map[K]V) *SafeSliceMap[K, V // The sort function is a Less function, that returns true when item 1 is "less" than item 2. // The sort function receives both the keys and values, so it can use either or both to decide how to sort. func (m *SafeSliceMap[K, V]) SetSortFunc(f func(key1, key2 K, val1, val2 V) bool) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.sm.SetSortFunc(f) } @@ -57,8 +57,8 @@ func (m *SafeSliceMap[K, V]) SetSortFunc(f func(key1, key2 K, val1, val2 V) bool // If the key already exists, the range order will not change. If you want the order // to change, call Delete first, and then Set. func (m *SafeSliceMap[K, V]) Set(key K, val V) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.sm.Set(key, val) } @@ -66,72 +66,72 @@ func (m *SafeSliceMap[K, V]) Set(key K, val V) { // If the index is bigger than // the length, it puts it at the end. Negative indexes are backwards from the end. func (m *SafeSliceMap[K, V]) SetAt(index int, key K, val V) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.sm.SetAt(index, key, val) } // Delete removes the item with the given key and returns the value. func (m *SafeSliceMap[K, V]) Delete(key K) (val V) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() return m.sm.Delete(key) } // Get returns the value based on its key. If the key does not exist, an empty value is returned. func (m *SafeSliceMap[K, V]) Get(key K) (val V) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Get(key) } // Load returns the value based on its key, and a boolean indicating whether it exists in the map. // This is the same interface as sync.Map.Load() func (m *SafeSliceMap[K, V]) Load(key K) (val V, ok bool) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Load(key) } // Has returns true if the given key exists in the map. func (m *SafeSliceMap[K, V]) Has(key K) (ok bool) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Has(key) } // GetAt returns the value based on its position. If the position is out of bounds, an empty value is returned. func (m *SafeSliceMap[K, V]) GetAt(position int) (val V) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.GetAt(position) } // GetKeyAt returns the key based on its position. If the position is out of bounds, an empty value is returned. func (m *SafeSliceMap[K, V]) GetKeyAt(position int) (key K) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.GetKeyAt(position) } // Values returns a slice of the values in the order they were added or sorted. func (m *SafeSliceMap[K, V]) Values() (values []V) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Values() } // Keys returns the keys of the map, in the order they were added or sorted. func (m *SafeSliceMap[K, V]) Keys() (keys []K) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Keys() } // Len returns the number of items in the map. func (m *SafeSliceMap[K, V]) Len() int { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Len() } @@ -139,23 +139,23 @@ func (m *SafeSliceMap[K, V]) Len() int { // If you are using a sort function, you must save and restore the sort function in a separate operation // since functions are not serializable. func (m *SafeSliceMap[K, V]) MarshalBinary() (data []byte, err error) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.MarshalBinary() } // UnmarshalBinary implements the BinaryUnmarshaler interface to convert a byte stream to a // SafeSliceMap. func (m *SafeSliceMap[K, V]) UnmarshalBinary(data []byte) (err error) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() return m.sm.UnmarshalBinary(data) } // MarshalJSON implements the json.Marshaler interface to convert the map into a JSON object. func (m *SafeSliceMap[K, V]) MarshalJSON() (data []byte, err error) { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() // Json objects are unordered return m.sm.MarshalJSON() @@ -164,8 +164,8 @@ func (m *SafeSliceMap[K, V]) MarshalJSON() (data []byte, err error) { // UnmarshalJSON implements the json.Unmarshaler interface to convert a json object to a Map. // The JSON must start with an object. func (m *SafeSliceMap[K, V]) UnmarshalJSON(data []byte) (err error) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() return m.sm.UnmarshalJSON(data) } @@ -194,8 +194,8 @@ func (m *SafeSliceMap[K, V]) Range(f func(key K, value V) bool) { if m == nil || m.sm.items == nil { // prevent unnecessary lock return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m.sm.Range(f) } @@ -204,16 +204,16 @@ func (m *SafeSliceMap[K, V]) Range(f func(key K, value V) bool) { // If the values are not comparable, you should implement the Equaler interface on the values. // Otherwise, you will get a runtime panic. func (m *SafeSliceMap[K, V]) Equal(m2 MapI[K, V]) bool { - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() return m.sm.Equal(m2) } // Clear removes all the items in the map. func (m *SafeSliceMap[K, V]) Clear() { - m.Lock() + m.mu.Lock() m.sm.Clear() - m.Unlock() + m.mu.Unlock() } // String outputs the map as a string. @@ -247,8 +247,8 @@ func (m *SafeSliceMap[K, V]) KeysIter() iter.Seq[K] { if m == nil || m.sm.items == nil { return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m.sm.KeysIter()(yield) } } @@ -261,8 +261,8 @@ func (m *SafeSliceMap[K, V]) ValuesIter() iter.Seq[V] { if m == nil || m.sm.items == nil { return } - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m.sm.ValuesIter()(yield) } } @@ -292,8 +292,8 @@ func CollectSafeSliceMap[K comparable, V any](seq iter.Seq2[K, V]) *SafeSliceMap // the new keys and values are set using ordinary assignment. The order is preserved. func (m *SafeSliceMap[K, V]) Clone() *SafeSliceMap[K, V] { m1 := new(SafeSliceMap[K, V]) - m.RLock() - defer m.RUnlock() + m.mu.RLock() + defer m.mu.RUnlock() m1.sm.items = m.sm.items.Clone() m1.sm.order = slices.Clone(m.sm.order) m1.sm.lessF = m.sm.lessF @@ -305,7 +305,7 @@ func (m *SafeSliceMap[K, V]) Clone() *SafeSliceMap[K, V] { // This function locks the entire slice structure for the entirety of the call, // so be careful to avoid deadlocks when calling this on a very big structure. func (m *SafeSliceMap[K, V]) DeleteFunc(del func(K, V) bool) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() m.sm.DeleteFunc(del) } diff --git a/slice_map_test.go b/slice_map_test.go index a752182..a38c9f8 100644 --- a/slice_map_test.go +++ b/slice_map_test.go @@ -217,3 +217,10 @@ func TestCollectSliceMap(t *testing.T) { expectedKeys := []string{"b", "a", "c"} assert.Equal(t, keys, expectedKeys) } + +// testing set with value-based SliceMap +func TestSliceMap_Set(t *testing.T) { + var s SliceMap[string, int] + s.Set("b", 2) + assert.Equal(t, 2, s.Get("b")) +}