Skip to content

Commit cd7d167

Browse files
committed
using ghetto code gen to make it easier, should move to an actual codegen package
1 parent 1e9c90a commit cd7d167

File tree

10 files changed

+668
-295
lines changed

10 files changed

+668
-295
lines changed

cmap.go

Lines changed: 128 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// AUTO-GENERATED by mapSpec.sh
2+
// DO NOT EDIT
3+
// generated from https://github.com/OneOfOne/cmap/tree/master/internal/cmap
4+
15
package cmap
26

37
import (
@@ -6,21 +10,16 @@ import (
610
)
711

812
type KV struct {
9-
Key KT
10-
Value VT
13+
Key interface{}
14+
Value interface{}
1115
}
1216

13-
// DefaultShardCount is the default number of shards to use when New() or NewFromJSON() are called.
14-
// The default is 256.
15-
const DefaultShardCount = 1 << 8
16-
1717
// CMap is a concurrent safe sharded map to scale on multiple cores.
1818
type CMap struct {
19-
shards []*lmap
19+
shards []*LockedMap
2020
// HashFn allows using a custom hash function that's used to determain the key's shard.
21-
// Defaults to DefaultKeyHasher
22-
HashFn func(KT) uint32
23-
21+
// Defaults to DefaultKeyHasher.
22+
HashFn func(interface{}) uint32
2423
keysPool sync.Pool
2524
mod uint32
2625
}
@@ -38,49 +37,43 @@ func NewSize(shardCount int) *CMap {
3837
} else if shardCount&(shardCount-1) != 0 {
3938
panic("shardCount must be a power of 2")
4039
}
41-
4240
cm := &CMap{
43-
shards: make([]*lmap, shardCount),
41+
shards: make([]*LockedMap, shardCount),
4442
mod: uint32(shardCount) - 1,
4543
HashFn: DefaultKeyHasher,
4644
}
47-
4845
for i := range cm.shards {
49-
cm.shards[i] = newLmap(shardCount)
46+
cm.shards[i] = NewLockedMapSize(shardCount)
5047
}
51-
5248
cm.keysPool.New = func() interface{} {
53-
out := make([]KT, 0, shardCount) // good starting round
54-
55-
return &out // return a ptr to avoid extra allocation on Get/Put
49+
out := make([]interface{}, 0, shardCount) // good starting round
50+
return &out // return a ptr to avoid extra allocation on Get/Put
5651
}
57-
5852
return cm
5953
}
60-
61-
func (cm *CMap) shard(key KT) *lmap {
54+
func (cm *CMap) shard(key interface{}) *LockedMap {
6255
h := cm.HashFn(key)
6356
return cm.shards[h&cm.mod]
6457
}
6558

6659
// Get is the equivalent of `val := map[key]`.
67-
func (cm *CMap) Get(key KT) (val VT) {
60+
func (cm *CMap) Get(key interface{}) (val interface{}) {
6861
return cm.shard(key).Get(key)
6962
}
7063

7164
// GetOK is the equivalent of `val, ok := map[key]`.
72-
func (cm *CMap) GetOK(key KT) (val VT, ok bool) {
65+
func (cm *CMap) GetOK(key interface{}) (val interface{}, ok bool) {
7366
return cm.shard(key).GetOK(key)
7467
}
7568

7669
// Set is the equivalent of `map[key] = val`.
77-
func (cm *CMap) Set(key KT, val VT) {
70+
func (cm *CMap) Set(key interface{}, val interface{}) {
7871
cm.shard(key).Set(key, val)
7972
}
8073

8174
// SetIfNotExists will only assign val to key if it wasn't already set.
8275
// Use `CMap.Update` if you need more logic.
83-
func (cm *CMap) SetIfNotExists(key KT, val VT) (set bool) {
76+
func (cm *CMap) SetIfNotExists(key interface{}, val interface{}) (set bool) {
8477
sh := cm.shard(key)
8578
sh.l.Lock()
8679
if _, ok := sh.m[key]; !ok {
@@ -91,28 +84,28 @@ func (cm *CMap) SetIfNotExists(key KT, val VT) (set bool) {
9184
}
9285

9386
// Has is the equivalent of `_, ok := map[key]`.
94-
func (cm *CMap) Has(key KT) bool { return cm.shard(key).Has(key) }
87+
func (cm *CMap) Has(key interface{}) bool { return cm.shard(key).Has(key) }
9588

9689
// Delete is the equivalent of `delete(map, key)`.
97-
func (cm *CMap) Delete(key KT) { cm.shard(key).Delete(key) }
90+
func (cm *CMap) Delete(key interface{}) { cm.shard(key).Delete(key) }
9891

9992
// DeleteAndGet is the equivalent of `oldVal := map[key]; delete(map, key)`.
100-
func (cm *CMap) DeleteAndGet(key KT) VT { return cm.shard(key).DeleteAndGet(key) }
93+
func (cm *CMap) DeleteAndGet(key interface{}) interface{} { return cm.shard(key).DeleteAndGet(key) }
10194

10295
// Update calls `fn` with the key's old value (or nil if it didn't exist) and assign the returned value to the key.
10396
// The shard containing the key will be locked, it is NOT safe to call other cmap funcs inside `fn`.
104-
func (cm *CMap) Update(key KT, fn func(oldval VT) (newval VT)) {
97+
func (cm *CMap) Update(key interface{}, fn func(oldval interface{}) (newval interface{})) {
10598
cm.shard(key).Update(key, fn)
10699
}
107100

108101
// Swap is the equivalent of `oldVal, map[key] = map[key], newVal`.
109-
func (cm *CMap) Swap(key KT, val VT) VT {
102+
func (cm *CMap) Swap(key interface{}, val interface{}) interface{} {
110103
return cm.shard(key).Swap(key, val)
111104
}
112105

113106
// Keys returns a slice of all the keys of the map.
114-
func (cm *CMap) Keys() []KT {
115-
out := make([]KT, 0, cm.Len())
107+
func (cm *CMap) Keys() []interface{} {
108+
out := make([]interface{}, 0, cm.Len())
116109
for i := range cm.shards {
117110
sh := cm.shards[i]
118111
sh.l.RLock()
@@ -125,11 +118,10 @@ func (cm *CMap) Keys() []KT {
125118
}
126119

127120
// ForEach loops over all the key/values in all the shards in order.
128-
// You can break early by returning an error.
121+
// You can break early by returning an error or Break.
129122
// It **is** safe to modify the map while using this iterator, however it uses more memory and is slightly slower.
130-
func (cm *CMap) ForEach(fn func(key KT, val VT) error) error {
131-
keysP := cm.keysPool.Get().(*[]KT)
132-
123+
func (cm *CMap) ForEach(fn func(key interface{}, val interface{}) error) error {
124+
keysP := cm.keysPool.Get().(*[]interface{})
133125
defer cm.keysPool.Put(keysP)
134126
for i := range cm.shards {
135127
keys := (*keysP)[:0]
@@ -144,9 +136,9 @@ func (cm *CMap) ForEach(fn func(key KT, val VT) error) error {
144136
}
145137

146138
// ForEachLocked loops over all the key/values in the map.
147-
// You can break early by returning an error.
139+
// You can break early by returning an error or Break.
148140
// It is **NOT* safe to modify the map while using this iterator.
149-
func (cm *CMap) ForEachLocked(fn func(key KT, val VT) error) error {
141+
func (cm *CMap) ForEachLocked(fn func(key interface{}, val interface{}) error) error {
150142
for i := range cm.shards {
151143
if err := cm.shards[i].ForEachLocked(fn); err != nil {
152144
if err == Break {
@@ -181,9 +173,8 @@ func (cm *CMap) IterLocked(ctx context.Context, buffer int) <-chan *KV {
181173
}()
182174
return ch
183175
}
184-
185176
func (cm *CMap) iterContext(ctx context.Context, ch chan<- *KV, locked bool) {
186-
fn := func(k KT, v VT) error {
177+
fn := func(k interface{}, v interface{}) error {
187178
select {
188179
case <-ctx.Done():
189180
return Break
@@ -209,3 +200,100 @@ func (cm *CMap) Len() int {
209200

210201
// NumShards returns the number of shards in the map.
211202
func (cm *CMap) NumShards() int { return len(cm.shards) }
203+
204+
type LockedMap struct {
205+
m map[interface{}]interface{}
206+
l *sync.RWMutex
207+
}
208+
209+
func NewLockedMap() *LockedMap {
210+
return NewLockedMapSize(0)
211+
}
212+
func NewLockedMapSize(cap int) *LockedMap {
213+
return &LockedMap{
214+
m: make(map[interface{}]interface{}, cap),
215+
l: new(sync.RWMutex),
216+
}
217+
}
218+
func (lm LockedMap) Set(key interface{}, v interface{}) {
219+
lm.l.Lock()
220+
lm.m[key] = v
221+
lm.l.Unlock()
222+
}
223+
func (lm LockedMap) Update(key interface{}, fn func(oldVal interface{}) (newVal interface{})) {
224+
lm.l.Lock()
225+
lm.m[key] = fn(lm.m[key])
226+
lm.l.Unlock()
227+
}
228+
func (lm LockedMap) Swap(key interface{}, newV interface{}) (oldV interface{}) {
229+
lm.l.Lock()
230+
oldV = lm.m[key]
231+
lm.m[key] = newV
232+
lm.l.Unlock()
233+
return
234+
}
235+
func (lm LockedMap) Get(key interface{}) (v interface{}) {
236+
lm.l.RLock()
237+
v = lm.m[key]
238+
lm.l.RUnlock()
239+
return
240+
}
241+
func (lm LockedMap) GetOK(key interface{}) (v interface{}, ok bool) {
242+
lm.l.RLock()
243+
v, ok = lm.m[key]
244+
lm.l.RUnlock()
245+
return
246+
}
247+
func (lm LockedMap) Has(key interface{}) (ok bool) {
248+
lm.l.RLock()
249+
_, ok = lm.m[key]
250+
lm.l.RUnlock()
251+
return
252+
}
253+
func (lm LockedMap) Delete(key interface{}) {
254+
lm.l.Lock()
255+
delete(lm.m, key)
256+
lm.l.Unlock()
257+
}
258+
func (lm LockedMap) DeleteAndGet(key interface{}) (v interface{}) {
259+
lm.l.Lock()
260+
v = lm.m[key]
261+
delete(lm.m, key)
262+
lm.l.Unlock()
263+
return v
264+
}
265+
func (lm LockedMap) Len() (ln int) {
266+
lm.l.RLock()
267+
ln = len(lm.m)
268+
lm.l.RUnlock()
269+
return
270+
}
271+
func (lm LockedMap) ForEach(keys []interface{}, fn func(key interface{}, val interface{}) error) (err error) {
272+
lm.l.RLock()
273+
for key := range lm.m {
274+
keys = append(keys, key)
275+
}
276+
lm.l.RUnlock()
277+
for _, key := range keys {
278+
lm.l.RLock()
279+
val, ok := lm.m[key]
280+
lm.l.RUnlock()
281+
if !ok {
282+
continue
283+
}
284+
if err = fn(key, val); err != nil {
285+
return
286+
}
287+
}
288+
return
289+
}
290+
func (lm LockedMap) ForEachLocked(fn func(key interface{}, val interface{}) error) (err error) {
291+
lm.l.RLock()
292+
defer lm.l.RUnlock()
293+
for key, val := range lm.m {
294+
if err = fn(key, val); err != nil {
295+
return
296+
}
297+
}
298+
return
299+
}

hashers/common.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package hashers
2+
3+
import (
4+
"fmt"
5+
"math"
6+
)
7+
8+
// KeyHasher is a type that provides its own hash function.
9+
type KeyHasher interface {
10+
Hash() uint64
11+
}
12+
13+
// TypeHasher32 returns a hash for the specific key for internal sharding.
14+
// By default, those types are supported as keys: KeyHasher, string, uint64, int64, uint32, int32, uint16, int16, uint8,
15+
// int8, uint, int, float64, float32 and fmt.Stringer.
16+
func TypeHasher32(v interface{}) uint32 {
17+
switch v := v.(type) {
18+
case KeyHasher:
19+
return MixHash32(uint32(v.Hash()))
20+
case string:
21+
return Fnv32(v)
22+
case int:
23+
return MixHash32(uint32(v))
24+
case uint:
25+
return MixHash32(uint32(v))
26+
case uint64:
27+
return MixHash32(uint32(v))
28+
case int64:
29+
return MixHash32(uint32(v))
30+
case uint32:
31+
return MixHash32(uint32(v))
32+
case int32:
33+
return MixHash32(uint32(v))
34+
case uint16:
35+
return MixHash32(uint32(v))
36+
case int16:
37+
return MixHash32(uint32(v))
38+
case uint8:
39+
return MixHash32(uint32(v))
40+
case int8:
41+
return MixHash32(uint32(v))
42+
case float64:
43+
return MixHash32(uint32(math.Float64bits(v)))
44+
case float32:
45+
return MixHash32(uint32(math.Float32bits(v)))
46+
case fmt.Stringer:
47+
return Fnv32(v.String())
48+
default:
49+
panic(fmt.Sprintf("unsupported type: %T (%v)", v, v))
50+
}
51+
}
52+
53+
// TypeHasher64 returns a hash for the specific key for internal sharding.
54+
// By default, those types are supported as keys: KeyHasher, string, uint64, int64, uint32, int32, uint16, int16, uint8,
55+
// int8, uint, int, float64, float32 and fmt.Stringer.
56+
func TypeHasher64(v interface{}) uint64 {
57+
switch v := v.(type) {
58+
case KeyHasher:
59+
return MixHash64(v.Hash())
60+
case string:
61+
return Fnv64(v)
62+
case int:
63+
return MixHash64(uint64(v))
64+
case uint:
65+
return MixHash64(uint64(v))
66+
case uint64:
67+
return MixHash64(v)
68+
case int64:
69+
return MixHash64(uint64(v))
70+
case uint32:
71+
return MixHash64(uint64(v))
72+
case int32:
73+
return MixHash64(uint64(v))
74+
case uint16:
75+
return MixHash64(uint64(v))
76+
case int16:
77+
return MixHash64(uint64(v))
78+
case uint8:
79+
return MixHash64(uint64(v))
80+
case int8:
81+
return MixHash64(uint64(v))
82+
case float64:
83+
return MixHash64(math.Float64bits(v))
84+
case float32:
85+
return MixHash64(uint64(math.Float32bits(v)))
86+
case fmt.Stringer:
87+
return Fnv64(v.String())
88+
default:
89+
panic(fmt.Sprintf("unsupported type: %T (%v)", v, v))
90+
}
91+
}
92+
93+
// MixHash32 mixes the hash to make sure the bits are spread, borrowed from xxhash.
94+
func MixHash32(h uint32) uint32 {
95+
const prime32x2 = 2246822519
96+
const prime32x3 = 3266489917
97+
h ^= h >> 15
98+
h *= prime32x2
99+
h ^= h >> 13
100+
h *= prime32x3
101+
h ^= h >> 16
102+
return h
103+
}
104+
105+
// MixHash64 mixes the hash to make sure the bits are spread, borrowed from xxhash.
106+
func MixHash64(h uint64) uint64 {
107+
const prime64x2 = 14029467366897019727
108+
const prime64x3 = 1609587929392839161
109+
110+
h ^= h >> 33
111+
h *= prime64x2
112+
h ^= h >> 29
113+
h *= prime64x3
114+
h ^= h >> 32
115+
return h
116+
}

0 commit comments

Comments
 (0)