-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsafe_slice_map.go
311 lines (275 loc) · 9.73 KB
/
safe_slice_map.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package maps
import (
"fmt"
"iter"
"slices"
"strings"
"sync"
)
// SafeSliceMap is a go map that uses a slice to save the order of its keys so that the map can
// be ranged in a predictable order. SafeSliceMap is safe for concurrent use.
//
// By default, the order will be the same order that items were inserted,
// i.e. a FIFO list, which is similar to how PHP arrays work. You can also define a sort function on the list
// to keep it sorted.
//
// The recommended way to create a SliceMap is to first declare a concrete type alias, and then call
// new on it, like this:
//
// type MyMap = SafeSliceMap[string,int]
//
// m := new(MyMap)
//
// This will allow you to swap in a different kind of Map just by changing the type.
//
// Call SetSortFunc to give the map a function that will keep the keys sorted in a particular order.
//
// Do not make a copy of a SafeSliceMap using the equality operator. Use Clone() instead.
type SafeSliceMap[K comparable, V any] struct {
sync.RWMutex
sm SliceMap[K, V]
}
// NewSafeSliceMap creates a new SafeSliceMap.
// Pass in zero or more standard maps and the contents of those maps will be copied to the new SafeSliceMap.
func NewSafeSliceMap[K comparable, V any](sources ...map[K]V) *SafeSliceMap[K, V] {
m := new(SafeSliceMap[K, V])
for _, i := range sources {
m.Copy(Cast(i))
}
return m
}
// SetSortFunc sets the sort function which will determine the order of the items in the map
// on an ongoing basis. Normally, items will iterate in the order they were added.
// 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.sm.SetSortFunc(f)
}
// Set sets the given key to the given value.
//
// 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.sm.Set(key, val)
}
// SetAt sets the given key to the given value, but also inserts it at the index specified.
// 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.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()
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()
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()
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()
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()
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()
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()
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()
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()
return m.sm.Len()
}
// MarshalBinary implements the BinaryMarshaler interface to convert the map to a byte stream.
// 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()
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()
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()
// Json objects are unordered
return m.sm.MarshalJSON()
}
// 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()
return m.sm.UnmarshalJSON(data)
}
// Merge the given map into the current one.
// Deprecated: Use copy instead.
func (m *SafeSliceMap[K, V]) Merge(in MapI[K, V]) {
m.Copy(in)
}
// Copy will copy the given map into the current one.
func (m *SafeSliceMap[K, V]) Copy(in MapI[K, V]) {
in.Range(func(k K, v V) bool {
m.Set(k, v) // This will lock and unlock, making sure that a long operation does not deadlock another go routine.
return true
})
}
// Range will call the given function with every key and value in the order
// they were placed in the map, or in if you sorted the map, in your custom order.
// If f returns false, it stops the iteration. This pattern is taken from sync.Map.
// During this process, the map will be locked, so do not pass a function that will take
// significant amounts of time, nor will call into other methods of the SafeSliceMap which might also need a lock.
// The workaround is to call Keys() and iterate over the returned copy of the keys, but making sure
// your function can handle the situation where the key no longer exists in the slice.
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.sm.Range(f)
}
// Equal returns true if all the keys and values are equal, regardless of the order.
//
// 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()
return m.sm.Equal(m2)
}
// Clear removes all the items in the map.
func (m *SafeSliceMap[K, V]) Clear() {
m.Lock()
m.sm.Clear()
m.Unlock()
}
// String outputs the map as a string.
func (m *SafeSliceMap[K, V]) String() string {
var s string
s = "{"
// Range will handle locking
m.Range(func(k K, v V) bool {
s += fmt.Sprintf(`%#v:%#v,`, k, v)
return true
})
s = strings.TrimRight(s, ",")
s += "}"
return s
}
// All returns an iterator over all the items in the map in the order they were entered or sorted.
func (m *SafeSliceMap[K, V]) All() iter.Seq2[K, V] {
return func(yield func(K, V) bool) {
m.Range(yield)
}
}
// KeysIter returns an iterator over all the keys in the map.
// During this process, the map will be locked, so do not pass a function that will take
// significant amounts of time, nor will call into other methods of the SafeSliceMap which might also need a lock.
func (m *SafeSliceMap[K, V]) KeysIter() iter.Seq[K] {
return func(yield func(K) bool) {
if m == nil || m.sm.items == nil {
return
}
m.RLock()
defer m.RUnlock()
m.sm.KeysIter()(yield)
}
}
// ValuesIter returns an iterator over all the values in the map.
// During this process, the map will be locked, so do not pass a function that will take
// significant amounts of time, nor will call into other methods of the SafeSliceMap which might also need a lock.
func (m *SafeSliceMap[K, V]) ValuesIter() iter.Seq[V] {
return func(yield func(V) bool) {
if m == nil || m.sm.items == nil {
return
}
m.RLock()
defer m.RUnlock()
m.sm.ValuesIter()(yield)
}
}
// Insert adds the values from seq to the end of the map.
// Duplicate keys are overridden but not moved.
// Will lock and unlock for each item in seq to give time to other go routines.
func (m *SafeSliceMap[K, V]) Insert(seq iter.Seq2[K, V]) {
for k, v := range seq {
m.Set(k, v)
}
}
// CollectSafeSliceMap collects key-value pairs from seq into a new SafeSliceMap
// and returns it.
func CollectSafeSliceMap[K comparable, V any](seq iter.Seq2[K, V]) *SafeSliceMap[K, V] {
m := new(SafeSliceMap[K, V])
// no need to lock here since this is a private variable
for k, v := range seq {
m.sm.Set(k, v)
}
return m
}
// Clone returns a copy of the SafeSliceMap. This is a shallow clone of the keys and values:
// 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()
m1.sm.items = m.sm.items.Clone()
m1.sm.order = slices.Clone(m.sm.order)
m1.sm.lessF = m.sm.lessF
return m1
}
// DeleteFunc deletes any key/value pairs for which del returns true.
// Items are ranged in order.
// 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.sm.DeleteFunc(del)
}