@@ -9,29 +9,29 @@ var SHARD_COUNT = 32
99
1010// A "thread" safe map of type string:Anything.
1111// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
12- type ConcurrentMap []* ConcurrentMapShared
12+ type ConcurrentMap [ V any ] []* ConcurrentMapShared [ V ]
1313
1414// A "thread" safe string to anything map.
15- type ConcurrentMapShared struct {
16- items map [string ]interface {}
15+ type ConcurrentMapShared [ V any ] struct {
16+ items map [string ]V
1717 sync.RWMutex // Read Write mutex, guards access to internal map.
1818}
1919
2020// Creates a new concurrent map.
21- func New () ConcurrentMap {
22- m := make (ConcurrentMap , SHARD_COUNT )
21+ func New [ V any ] () ConcurrentMap [ V ] {
22+ m := make (ConcurrentMap [ V ] , SHARD_COUNT )
2323 for i := 0 ; i < SHARD_COUNT ; i ++ {
24- m [i ] = & ConcurrentMapShared {items : make (map [string ]interface {} )}
24+ m [i ] = & ConcurrentMapShared [ V ] {items : make (map [string ]V )}
2525 }
2626 return m
2727}
2828
2929// GetShard returns shard under given key
30- func (m ConcurrentMap ) GetShard (key string ) * ConcurrentMapShared {
30+ func (m ConcurrentMap [ V ] ) GetShard (key string ) * ConcurrentMapShared [ V ] {
3131 return m [uint (fnv32 (key ))% uint (SHARD_COUNT )]
3232}
3333
34- func (m ConcurrentMap ) MSet (data map [string ]interface {} ) {
34+ func (m ConcurrentMap [ V ] ) MSet (data map [string ]V ) {
3535 for key , value := range data {
3636 shard := m .GetShard (key )
3737 shard .Lock ()
@@ -41,7 +41,7 @@ func (m ConcurrentMap) MSet(data map[string]interface{}) {
4141}
4242
4343// Sets the given value under the specified key.
44- func (m ConcurrentMap ) Set (key string , value interface {} ) {
44+ func (m ConcurrentMap [ V ] ) Set (key string , value V ) {
4545 // Get map shard.
4646 shard := m .GetShard (key )
4747 shard .Lock ()
@@ -53,10 +53,10 @@ func (m ConcurrentMap) Set(key string, value interface{}) {
5353// It is called while lock is held, therefore it MUST NOT
5454// try to access other keys in same map, as it can lead to deadlock since
5555// Go sync.RWLock is not reentrant
56- type UpsertCb func (exist bool , valueInMap interface {} , newValue interface {}) interface {}
56+ type UpsertCb [ V any ] func (exist bool , valueInMap V , newValue V ) V
5757
5858// Insert or Update - updates existing element or inserts a new one using UpsertCb
59- func (m ConcurrentMap ) Upsert (key string , value interface {} , cb UpsertCb ) (res interface {} ) {
59+ func (m ConcurrentMap [ V ] ) Upsert (key string , value V , cb UpsertCb [ V ] ) (res V ) {
6060 shard := m .GetShard (key )
6161 shard .Lock ()
6262 v , ok := shard .items [key ]
@@ -67,7 +67,7 @@ func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res i
6767}
6868
6969// Sets the given value under the specified key if no value was associated with it.
70- func (m ConcurrentMap ) SetIfAbsent (key string , value interface {} ) bool {
70+ func (m ConcurrentMap [ V ] ) SetIfAbsent (key string , value V ) bool {
7171 // Get map shard.
7272 shard := m .GetShard (key )
7373 shard .Lock ()
@@ -80,7 +80,7 @@ func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
8080}
8181
8282// Get retrieves an element from map under given key.
83- func (m ConcurrentMap ) Get (key string ) (interface {} , bool ) {
83+ func (m ConcurrentMap [ V ] ) Get (key string ) (V , bool ) {
8484 // Get shard
8585 shard := m .GetShard (key )
8686 shard .RLock ()
@@ -91,7 +91,7 @@ func (m ConcurrentMap) Get(key string) (interface{}, bool) {
9191}
9292
9393// Count returns the number of elements within the map.
94- func (m ConcurrentMap ) Count () int {
94+ func (m ConcurrentMap [ V ] ) Count () int {
9595 count := 0
9696 for i := 0 ; i < SHARD_COUNT ; i ++ {
9797 shard := m [i ]
@@ -103,7 +103,7 @@ func (m ConcurrentMap) Count() int {
103103}
104104
105105// Looks up an item under specified key
106- func (m ConcurrentMap ) Has (key string ) bool {
106+ func (m ConcurrentMap [ V ] ) Has (key string ) bool {
107107 // Get shard
108108 shard := m .GetShard (key )
109109 shard .RLock ()
@@ -114,7 +114,7 @@ func (m ConcurrentMap) Has(key string) bool {
114114}
115115
116116// Remove removes an element from the map.
117- func (m ConcurrentMap ) Remove (key string ) {
117+ func (m ConcurrentMap [ V ] ) Remove (key string ) {
118118 // Try to get shard.
119119 shard := m .GetShard (key )
120120 shard .Lock ()
@@ -124,12 +124,12 @@ func (m ConcurrentMap) Remove(key string) {
124124
125125// RemoveCb is a callback executed in a map.RemoveCb() call, while Lock is held
126126// If returns true, the element will be removed from the map
127- type RemoveCb func (key string , v interface {} , exists bool ) bool
127+ type RemoveCb [ V any ] func (key string , v V , exists bool ) bool
128128
129129// RemoveCb locks the shard containing the key, retrieves its current value and calls the callback with those params
130130// If callback returns true and element exists, it will remove it from the map
131131// Returns the value returned by the callback (even if element was not present in the map)
132- func (m ConcurrentMap ) RemoveCb (key string , cb RemoveCb ) bool {
132+ func (m ConcurrentMap [ V ] ) RemoveCb (key string , cb RemoveCb [ V ] ) bool {
133133 // Try to get shard.
134134 shard := m .GetShard (key )
135135 shard .Lock ()
@@ -143,7 +143,7 @@ func (m ConcurrentMap) RemoveCb(key string, cb RemoveCb) bool {
143143}
144144
145145// Pop removes an element from the map and returns it
146- func (m ConcurrentMap ) Pop (key string ) (v interface {} , exists bool ) {
146+ func (m ConcurrentMap [ V ] ) Pop (key string ) (v V , exists bool ) {
147147 // Try to get shard.
148148 shard := m .GetShard (key )
149149 shard .Lock ()
@@ -154,40 +154,40 @@ func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) {
154154}
155155
156156// IsEmpty checks if map is empty.
157- func (m ConcurrentMap ) IsEmpty () bool {
157+ func (m ConcurrentMap [ V ] ) IsEmpty () bool {
158158 return m .Count () == 0
159159}
160160
161161// Used by the Iter & IterBuffered functions to wrap two variables together over a channel,
162- type Tuple struct {
162+ type Tuple [ V any ] struct {
163163 Key string
164- Val interface {}
164+ Val V
165165}
166166
167167// Iter returns an iterator which could be used in a for range loop.
168168//
169169// Deprecated: using IterBuffered() will get a better performence
170- func (m ConcurrentMap ) Iter () <- chan Tuple {
170+ func (m ConcurrentMap [ V ] ) Iter () <- chan Tuple [ V ] {
171171 chans := snapshot (m )
172- ch := make (chan Tuple )
172+ ch := make (chan Tuple [ V ] )
173173 go fanIn (chans , ch )
174174 return ch
175175}
176176
177177// IterBuffered returns a buffered iterator which could be used in a for range loop.
178- func (m ConcurrentMap ) IterBuffered () <- chan Tuple {
178+ func (m ConcurrentMap [ V ] ) IterBuffered () <- chan Tuple [ V ] {
179179 chans := snapshot (m )
180180 total := 0
181181 for _ , c := range chans {
182182 total += cap (c )
183183 }
184- ch := make (chan Tuple , total )
184+ ch := make (chan Tuple [ V ] , total )
185185 go fanIn (chans , ch )
186186 return ch
187187}
188188
189189// Clear removes all items from map.
190- func (m ConcurrentMap ) Clear () {
190+ func (m ConcurrentMap [ V ] ) Clear () {
191191 for item := range m .IterBuffered () {
192192 m .Remove (item .Key )
193193 }
@@ -197,23 +197,23 @@ func (m ConcurrentMap) Clear() {
197197// which likely takes a snapshot of `m`.
198198// It returns once the size of each buffered channel is determined,
199199// before all the channels are populated using goroutines.
200- func snapshot (m ConcurrentMap ) (chans []chan Tuple ) {
200+ func snapshot [ V any ] (m ConcurrentMap [ V ] ) (chans []chan Tuple [ V ] ) {
201201 //When you access map items before initializing.
202- if len (m ) == 0 {
202+ if len (m ) == 0 {
203203 panic (`cmap.ConcurrentMap is not initialized. Should run New() before usage.` )
204204 }
205- chans = make ([]chan Tuple , SHARD_COUNT )
205+ chans = make ([]chan Tuple [ V ] , SHARD_COUNT )
206206 wg := sync.WaitGroup {}
207207 wg .Add (SHARD_COUNT )
208208 // Foreach shard.
209209 for index , shard := range m {
210- go func (index int , shard * ConcurrentMapShared ) {
210+ go func (index int , shard * ConcurrentMapShared [ V ] ) {
211211 // Foreach key, value pair.
212212 shard .RLock ()
213- chans [index ] = make (chan Tuple , len (shard .items ))
213+ chans [index ] = make (chan Tuple [ V ] , len (shard .items ))
214214 wg .Done ()
215215 for key , val := range shard .items {
216- chans [index ] <- Tuple {key , val }
216+ chans [index ] <- Tuple [ V ] {key , val }
217217 }
218218 shard .RUnlock ()
219219 close (chans [index ])
@@ -224,11 +224,11 @@ func snapshot(m ConcurrentMap) (chans []chan Tuple) {
224224}
225225
226226// fanIn reads elements from channels `chans` into channel `out`
227- func fanIn (chans []chan Tuple , out chan Tuple ) {
227+ func fanIn [ V any ] (chans []chan Tuple [ V ] , out chan Tuple [ V ] ) {
228228 wg := sync.WaitGroup {}
229229 wg .Add (len (chans ))
230230 for _ , ch := range chans {
231- go func (ch chan Tuple ) {
231+ go func (ch chan Tuple [ V ] ) {
232232 for t := range ch {
233233 out <- t
234234 }
@@ -239,9 +239,9 @@ func fanIn(chans []chan Tuple, out chan Tuple) {
239239 close (out )
240240}
241241
242- // Items returns all items as map[string]interface{}
243- func (m ConcurrentMap ) Items () map [string ]interface {} {
244- tmp := make (map [string ]interface {} )
242+ // Items returns all items as map[string]V
243+ func (m ConcurrentMap [ V ] ) Items () map [string ]V {
244+ tmp := make (map [string ]V )
245245
246246 // Insert items to temporary map.
247247 for item := range m .IterBuffered () {
@@ -251,15 +251,15 @@ func (m ConcurrentMap) Items() map[string]interface{} {
251251 return tmp
252252}
253253
254- // Iterator callback,called for every key,value found in
254+ // Iterator callbacalled for every key,value found in
255255// maps. RLock is held for all calls for a given shard
256256// therefore callback sess consistent view of a shard,
257257// but not across the shards
258- type IterCb func (key string , v interface {} )
258+ type IterCb [ V any ] func (key string , v V )
259259
260260// Callback based iterator, cheapest way to read
261261// all elements in a map.
262- func (m ConcurrentMap ) IterCb (fn IterCb ) {
262+ func (m ConcurrentMap [ V ] ) IterCb (fn IterCb [ V ] ) {
263263 for idx := range m {
264264 shard := (m )[idx ]
265265 shard .RLock ()
@@ -271,15 +271,15 @@ func (m ConcurrentMap) IterCb(fn IterCb) {
271271}
272272
273273// Keys returns all keys as []string
274- func (m ConcurrentMap ) Keys () []string {
274+ func (m ConcurrentMap [ V ] ) Keys () []string {
275275 count := m .Count ()
276276 ch := make (chan string , count )
277277 go func () {
278278 // Foreach shard.
279279 wg := sync.WaitGroup {}
280280 wg .Add (SHARD_COUNT )
281281 for _ , shard := range m {
282- go func (shard * ConcurrentMapShared ) {
282+ go func (shard * ConcurrentMapShared [ V ] ) {
283283 // Foreach key, value pair.
284284 shard .RLock ()
285285 for key := range shard .items {
@@ -302,9 +302,9 @@ func (m ConcurrentMap) Keys() []string {
302302}
303303
304304//Reviles ConcurrentMap "private" variables to json marshal.
305- func (m ConcurrentMap ) MarshalJSON () ([]byte , error ) {
305+ func (m ConcurrentMap [ V ] ) MarshalJSON () ([]byte , error ) {
306306 // Create a temporary map, which will hold all item spread across shards.
307- tmp := make (map [string ]interface {} )
307+ tmp := make (map [string ]V )
308308
309309 // Insert items to temporary map.
310310 for item := range m .IterBuffered () {
@@ -326,13 +326,13 @@ func fnv32(key string) uint32 {
326326
327327// Concurrent map uses Interface{} as its value, therefor JSON Unmarshal
328328// will probably won't know which to type to unmarshal into, in such case
329- // we'll end up with a value of type map[string]interface{} , In most cases this isn't
329+ // we'll end up with a value of type map[string]V , In most cases this isn't
330330// out value type, this is why we've decided to remove this functionality.
331331
332332// func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) {
333333// // Reverse process of Marshal.
334334
335- // tmp := make(map[string]interface{} )
335+ // tmp := make(map[string]V )
336336
337337// // Unmarshal into a single map.
338338// if err := json.Unmarshal(b, &tmp); err != nil {
0 commit comments