Skip to content

Commit 5d1f371

Browse files
committed
nuke the HashFn field since it's easy to generate typed version now.
Also add smart defaults for numbers / strings.
1 parent a7fb481 commit 5d1f371

File tree

6 files changed

+69
-21
lines changed

6 files changed

+69
-21
lines changed

cmap.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ package cmap
55
import (
66
"context"
77
"sync"
8-
9-
"github.com/OneOfOne/cmap/hashers"
108
)
119

1210
type (
@@ -21,8 +19,6 @@ const DefaultShardCount = 1 << 8
2119
type CMap struct {
2220
shards []*LMap
2321
keysPool sync.Pool
24-
// HashFn allows using a custom hash function that's used to determain the key's shard. Defaults to DefaultKeyHasher.
25-
HashFn func(KT) uint32
2622
}
2723

2824
// New is an alias for NewSize(DefaultShardCount)
@@ -41,7 +37,6 @@ func NewSize(shardCount int) *CMap {
4137

4238
cm := &CMap{
4339
shards: make([]*LMap, shardCount),
44-
HashFn: DefaultKeyHasher,
4540
}
4641

4742
cm.keysPool.New = func() interface{} {
@@ -59,63 +54,63 @@ func NewSize(shardCount int) *CMap {
5954

6055
// ShardForKey returns the LMap that may hold the specific key.
6156
func (cm *CMap) ShardForKey(key KT) *LMap {
62-
h := cm.HashFn(key)
57+
h := hasher(key)
6358
return cm.shards[h&uint32(len(cm.shards)-1)]
6459
}
6560

6661
// Set is the equivalent of `map[key] = val`.
6762
func (cm *CMap) Set(key KT, val VT) {
68-
h := cm.HashFn(key)
63+
h := hasher(key)
6964
cm.shards[h&uint32(len(cm.shards)-1)].Set(key, val)
7065
}
7166

7267
// SetIfNotExists will only assign val to key if it wasn't already set.
7368
// Use `Update` if you need more logic.
7469
func (cm *CMap) SetIfNotExists(key KT, val VT) (set bool) {
75-
h := cm.HashFn(key)
70+
h := hasher(key)
7671
return cm.shards[h&uint32(len(cm.shards)-1)].SetIfNotExists(key, val)
7772
}
7873

7974
// Get is the equivalent of `val := map[key]`.
8075
func (cm *CMap) Get(key KT) (val VT) {
81-
h := cm.HashFn(key)
76+
h := hasher(key)
8277
return cm.shards[h&uint32(len(cm.shards)-1)].Get(key)
8378
}
8479

8580
// GetOK is the equivalent of `val, ok := map[key]`.
8681
func (cm *CMap) GetOK(key KT) (val VT, ok bool) {
87-
h := cm.HashFn(key)
82+
h := hasher(key)
8883
return cm.shards[h&uint32(len(cm.shards)-1)].GetOK(key)
8984
}
9085

9186
// Has is the equivalent of `_, ok := map[key]`.
9287
func (cm *CMap) Has(key KT) bool {
93-
h := cm.HashFn(key)
88+
h := hasher(key)
9489
return cm.shards[h&uint32(len(cm.shards)-1)].Has(key)
9590
}
9691

9792
// Delete is the equivalent of `delete(map, key)`.
9893
func (cm *CMap) Delete(key KT) {
99-
h := cm.HashFn(key)
94+
h := hasher(key)
10095
cm.shards[h&uint32(len(cm.shards)-1)].Delete(key)
10196
}
10297

10398
// DeleteAndGet is the equivalent of `oldVal := map[key]; delete(map, key)`.
10499
func (cm *CMap) DeleteAndGet(key KT) VT {
105-
h := cm.HashFn(key)
100+
h := hasher(key)
106101
return cm.shards[h&uint32(len(cm.shards)-1)].DeleteAndGet(key)
107102
}
108103

109104
// Update calls `fn` with the key's old value (or nil) and assign the returned value to the key.
110105
// The shard containing the key will be locked, it is NOT safe to call other cmap funcs inside `fn`.
111106
func (cm *CMap) Update(key KT, fn func(oldval VT) (newval VT)) {
112-
h := cm.HashFn(key)
107+
h := hasher(key)
113108
cm.shards[h&uint32(len(cm.shards)-1)].Update(key, fn)
114109
}
115110

116111
// Swap is the equivalent of `oldVal, map[key] = map[key], newVal`.
117112
func (cm *CMap) Swap(key KT, val VT) VT {
118-
h := cm.HashFn(key)
113+
h := hasher(key)
119114
return cm.shards[h&uint32(len(cm.shards)-1)].Swap(key, val)
120115
}
121116

@@ -167,6 +162,19 @@ func (cm *CMap) Len() int {
167162
return ln
168163
}
169164

165+
// ShardDistribution returns the distribution of data amoung all shards.
166+
// Useful for debugging the efficiency of a hash.
167+
func (cm *CMap) ShardDistribution() []float64 {
168+
var (
169+
out = make([]float64, len(cm.shards))
170+
ln = float64(cm.Len())
171+
)
172+
for i := range out {
173+
out[i] = float64(cm.shards[i].Len()) / ln
174+
}
175+
return out
176+
}
177+
170178
// KV holds the key/value returned when Iter is called.
171179
type KV struct {
172180
Key KT
@@ -217,6 +225,3 @@ func (cm *CMap) iterContext(ctx context.Context, ch chan<- *KV, locked bool) {
217225

218226
// NumShards returns the number of shards in the map.
219227
func (cm *CMap) NumShards() int { return len(cm.shards) }
220-
221-
// DefaultKeyHasher is an alias for hashers.TypeHasher32(key).
222-
func DefaultKeyHasher(key KT) uint32 { return hashers.TypeHasher32(key) }

cmap_go18_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ func init() {
1717
}
1818
}
1919

20+
func TestDistruption(t *testing.T) {
21+
cm := cmap.NewSize(32)
22+
for i := 0; i < 1e6; i++ {
23+
cm.Set(i, i)
24+
}
25+
26+
t.Logf("%+v", cm.ShardDistribution())
27+
28+
}
29+
2030
func TestIter(t *testing.T) {
2131
cm := cmap.New()
2232
for i := 0; i < 100; i++ {

cmap_if_number.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// +build genx
2+
// +build genx_kt_int genx_kt_uint genx_kt_int32 genx_kt_uint32 genx_kt_int64 genx_kt_uint64 genx_kt_float64 genx_kt_float32
3+
4+
package cmap
5+
6+
import "github.com/OneOfOne/cmap/hashers"
7+
8+
func hasher(key KT) uint32 { return hashers.Mix32(uint32(key)) }

cmap_if_other.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// +build genx
2+
// +build !genx_kt_int
3+
// +build !genx_kt_uint
4+
// +build !genx_kt_int32
5+
// +build !genx_kt_uint32
6+
// +build !genx_kt_int64
7+
// +build !genx_kt_uint64
8+
// +build !genx_kt_float64
9+
// +build !genx_kt_float32
10+
// +build !genx_kt_string
11+
12+
package cmap
13+
14+
import "github.com/OneOfOne/cmap/hashers"
15+
16+
func hasher(key KT) uint32 { return hashers.TypeHasher32(key) }

cmap_if_string.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// +build genx
2+
// +build genx_kt_string
3+
4+
package cmap
5+
6+
import "github.com/OneOfOne/cmap/hashers"
7+
8+
func hasher(key KT) uint32 { return hashers.TypeHasher32(key) }

utils.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//go:generate go install github.com/OneOfOne/genx/...
2-
//go:generate genx -pkg github.com/OneOfOne/cmap -v -t KT=interface{},VT=interface{} -m -o ./cmap_iface_iface.go
3-
//go:generate genx -pkg github.com/OneOfOne/cmap -v -n stringcmap -t KT=string,VT=interface{} -fld HashFn -fn DefaultKeyHasher -s "cm.HashFn=hashers.Fnv32" -m -o ./stringcmap/cmap_string_iface.go
4-
//go:generate gometalinter --vendored-linters --aggregate --cyclo-over=17 ./...
2+
//go:generate genx -pkg github.com/OneOfOne/cmap -v -m -t KT=interface{},VT=interface{} -o ./cmap_iface_iface.go
3+
//go:generate genx -pkg github.com/OneOfOne/cmap -v -m -n stringcmap -t KT=string,VT=interface{} -o ./stringcmap/cmap_string_iface.go
4+
//go:generate genx -pkg github.com/OneOfOne/cmap -v -m -n uint64cmap -t KT=uint64,VT=interface{} -o ./uint64cmap/cmap_uint64_iface.go
5+
//go:generate gometalinter --aggregate --cyclo-over=17 ./...
56

67
package cmap

0 commit comments

Comments
 (0)