@@ -6,6 +6,13 @@ import (
6
6
"github.com/OneOfOne/cmap"
7
7
)
8
8
9
+ type (
10
+ KV struct {
11
+ Key string
12
+ Value interface {}
13
+ }
14
+ )
15
+
9
16
// DefaultShardCount is the default number of shards to use when New() or NewFromJSON() are called.
10
17
// The default is 256.
11
18
const DefaultShardCount = cmap .DefaultShardCount
@@ -42,58 +49,56 @@ func NewSize(shardCount int) *CMap {
42
49
return cm
43
50
}
44
51
45
- func (cm * CMap ) shard (key string ) * lmap {
52
+ func (cm * CMap ) shardForKey (key string ) * lmap {
46
53
h := cmap .Fnv32 (key )
47
54
return & cm .shards [h & cm .mod ]
48
55
}
49
56
50
57
// Get is the equivalent of `val := map[key]`.
51
58
func (cm * CMap ) Get (key string ) (val interface {}) {
52
- return cm .shard (key ).Get (key )
59
+ return cm .shardForKey (key ).Get (key )
53
60
}
54
61
55
62
// GetOK is the equivalent of `val, ok := map[key]`.
56
63
func (cm * CMap ) GetOK (key string ) (val interface {}, ok bool ) {
57
- return cm .shard (key ).GetOK (key )
64
+ return cm .shardForKey (key ).GetOK (key )
58
65
}
59
66
60
67
// Set is the equivalent of `map[key] = val`.
61
68
func (cm * CMap ) Set (key string , val interface {}) {
62
- cm .shard (key ).Set (key , val )
69
+ cm .shardForKey (key ).Set (key , val )
63
70
}
64
71
65
72
// SetIfNotExists will only assign val to key if it wasn't already set.
66
73
// Use `CMap.Update` if you need more logic.
67
74
func (cm * CMap ) SetIfNotExists (key string , val interface {}) (set bool ) {
68
75
cm .Update (key , func (oldVal interface {}) (newVal interface {}) {
69
- switch oldVal .(type ) {
70
- case nil :
76
+ if set = oldVal == nil ; set {
71
77
return newVal
72
- default :
73
- return oldVal
74
78
}
79
+ return oldVal
75
80
})
76
81
return
77
82
}
78
83
79
84
// Has is the equivalent of `_, ok := map[key]`.
80
- func (cm * CMap ) Has (key string ) bool { return cm .shard (key ).Has (key ) }
85
+ func (cm * CMap ) Has (key string ) bool { return cm .shardForKey (key ).Has (key ) }
81
86
82
87
// Delete is the equivalent of `delete(map, key)`.
83
- func (cm * CMap ) Delete (key string ) { cm .shard (key ).Delete (key ) }
88
+ func (cm * CMap ) Delete (key string ) { cm .shardForKey (key ).Delete (key ) }
84
89
85
90
// DeleteAndGet is the equivalent of `oldVal := map[key]; delete(map, key)`.
86
- func (cm * CMap ) DeleteAndGet (key string ) interface {} { return cm .shard (key ).DeleteAndGet (key ) }
91
+ func (cm * CMap ) DeleteAndGet (key string ) interface {} { return cm .shardForKey (key ).DeleteAndGet (key ) }
87
92
88
93
// Update calls `fn` with the key's old value (or nil if it didn't exist) and assign the returned value to the key.
89
94
// The shard containing the key will be locked, it is NOT safe to call other cmap funcs inside `fn`.
90
95
func (cm * CMap ) Update (key string , fn func (oldval interface {}) (newval interface {})) {
91
- cm .shard (key ).Update (key , fn )
96
+ cm .shardForKey (key ).Update (key , fn )
92
97
}
93
98
94
99
// Swap is the equivalent of `oldVal, map[key] = map[key], newVal`.
95
100
func (cm * CMap ) Swap (key string , val interface {}) interface {} {
96
- return cm .shard (key ).Swap (key , val )
101
+ return cm .shardForKey (key ).Swap (key , val )
97
102
}
98
103
99
104
// Keys returns a slice of all the keys of the map.
@@ -112,12 +117,12 @@ func (cm *CMap) Keys() []string {
112
117
113
118
// ForEach loops over all the key/values in all the shards in order.
114
119
// You can break early by returning an error.
115
- // it is safe to change the map inside fn .
120
+ // It **is** safe to modify the map while using this iterator, however it uses more memory and is slightly slower .
116
121
func (cm * CMap ) ForEach (fn func (key string , val interface {}) error ) error {
117
122
for i := range cm .shards {
118
123
if err := cm .shards [i ].ForEach (fn ); err != nil {
119
124
if err == cmap .Break {
120
- err = nil
125
+ return nil
121
126
}
122
127
return err
123
128
}
@@ -127,10 +132,10 @@ func (cm *CMap) ForEach(fn func(key string, val interface{}) error) error {
127
132
128
133
// ForEachLocked loops over all the key/values in the map.
129
134
// You can break early by returning an error.
130
- // It is **NOT** safe to change the map during this call .
135
+ // It is **NOT* safe to modify the map while using this iterator .
131
136
func (cm * CMap ) ForEachLocked (fn func (key string , val interface {}) error ) error {
132
137
for i := range cm .shards {
133
- if err := cm .shards [i ].ForEach (fn ); err != nil {
138
+ if err := cm .shards [i ].ForEachLocked (fn ); err != nil {
134
139
if err == cmap .Break {
135
140
return nil
136
141
}
@@ -140,34 +145,43 @@ func (cm *CMap) ForEachLocked(fn func(key string, val interface{}) error) error
140
145
return nil
141
146
}
142
147
143
- // KV is returned from the Iter channel.
144
- type KV struct {
145
- Key string
146
- Value interface {}
147
- }
148
-
149
148
// Iter returns a channel to be used in for range.
150
- // **Warning** breaking early will leak up to cm.NumShards() goroutines, use IterWithCancel if you intend to break early.
151
- // It is safe to modify the map while using this iterator.
149
+ // Use `context.WithCancel` if you intend to break early or goroutines will leak .
150
+ // It **is** safe to modify the map while using this iterator, however it uses more memory and is slightly slower .
152
151
func (cm * CMap ) Iter (ctx context.Context , buffer int ) <- chan * KV {
153
152
ch := make (chan * KV , buffer )
154
153
go func () {
155
- cm .iterContext (ctx , ch )
154
+ cm .iterContext (ctx , ch , false )
156
155
close (ch )
157
156
}()
158
157
return ch
159
158
}
160
159
161
- func (cm * CMap ) iterContext (ctx context.Context , ch chan <- * KV ) {
162
- for i := range cm .shards {
163
- cm .shards [i ].ForEach (func (k string , v interface {}) error {
164
- select {
165
- case <- ctx .Done ():
166
- return cmap .Break
167
- case ch <- & KV {k , v }:
168
- return nil
169
- }
170
- })
160
+ // IterLocked returns a channel to be used in for range.
161
+ // Use `context.WithCancel` if you intend to break early or goroutines will leak and map access will deadlock.
162
+ // It is **NOT* safe to modify the map while using this iterator.
163
+ func (cm * CMap ) IterLocked (ctx context.Context , buffer int ) <- chan * KV {
164
+ ch := make (chan * KV , buffer )
165
+ go func () {
166
+ cm .iterContext (ctx , ch , false )
167
+ close (ch )
168
+ }()
169
+ return ch
170
+ }
171
+
172
+ func (cm * CMap ) iterContext (ctx context.Context , ch chan <- * KV , locked bool ) {
173
+ fn := func (k string , v interface {}) error {
174
+ select {
175
+ case <- ctx .Done ():
176
+ return cmap .Break
177
+ case ch <- & KV {k , v }:
178
+ return nil
179
+ }
180
+ }
181
+ if locked {
182
+ cm .ForEachLocked (fn )
183
+ } else {
184
+ cm .ForEach (fn )
171
185
}
172
186
}
173
187
0 commit comments