Skip to content

Commit

Permalink
Merge pull request #7 from lithammer/hasher-concurrency-issue
Browse files Browse the repository at this point in the history
Deprecate old hashers since they're not safe for concurrency
  • Loading branch information
lithammer committed May 13, 2019
2 parents 33b8bda + 327fde3 commit fa2de10
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -20,13 +20,13 @@ func main() {
Includes a helper function for using a `string` as key instead of an `uint64`. This requires a hasher that computes the string into a format accepted by `Hash()`. Such a hasher that uses [CRC-64 (ECMA)](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) is also included for convenience.

```go
h := jump.HashString("127.0.0.1", 8, jump.CRC64) // h = 7
h := jump.HashString("127.0.0.1", 8, jump.NewCRC64()) // h = 7
```

In reality though you probably want to use a `Hasher` so you won't have to repeat the bucket size and which key hasher used. It also uses more convenient types, like `int` instead of `int32`.

```go
hasher := jump.New(8, jump.CRC64)
hasher := jump.New(8, jump.NewCRC64())
h := hasher.Hash("127.0.0.1") // h = 7
```

Expand Down
2 changes: 2 additions & 0 deletions doc.go
@@ -1,4 +1,6 @@
/*
Package jump implements the "jump consistent hash" algorithm.
Example
h := jump.Hash(256, 1024) // h = 520
Expand Down
13 changes: 10 additions & 3 deletions jump.go
Expand Up @@ -77,12 +77,19 @@ func (h *Hasher) Hash(key string) int {
var (
// CRC32 uses the 32-bit Cyclic Redundancy Check (CRC-32) with the IEEE
// polynomial.
CRC32 hash.Hash64 = &crc32Hasher{crc32.NewIEEE()}
NewCRC32 func() hash.Hash64 = func() hash.Hash64 { return &crc32Hasher{crc32.NewIEEE()} }
// CRC64 uses the 64-bit Cyclic Redundancy Check (CRC-64) with the ECMA
// polynomial.
CRC64 hash.Hash64 = crc64.New(crc64.MakeTable(crc64.ECMA))
NewCRC64 func() hash.Hash64 = func() hash.Hash64 { return crc64.New(crc64.MakeTable(crc64.ECMA)) }
// FNV1 uses the non-cryptographic hash function FNV-1.
FNV1 hash.Hash64 = fnv.New64()
NewFNV1 func() hash.Hash64 = func() hash.Hash64 { return fnv.New64() }
// FNV1a uses the non-cryptographic hash function FNV-1a.
NewFNV1a func() hash.Hash64 = func() hash.Hash64 { return fnv.New64a() }

// These are deprecated because they're not safe for concurrent use. Please
// use the New* functions instead.
CRC32 hash.Hash64 = &crc32Hasher{crc32.NewIEEE()}
CRC64 hash.Hash64 = crc64.New(crc64.MakeTable(crc64.ECMA))
FNV1 hash.Hash64 = fnv.New64()
FNV1a hash.Hash64 = fnv.New64a()
)
26 changes: 13 additions & 13 deletions jump_test.go
Expand Up @@ -35,19 +35,19 @@ func TestJumpHash(t *testing.T) {
var jumpStringTestVectors = []struct {
key string
buckets int32
hasher hash.Hash64
hasher func() hash.Hash64
expected int32
}{
{"localhost", 10, CRC32, 9},
{"ёлка", 10, CRC64, 6},
{"ветер", 10, FNV1, 3},
{"中国", 10, FNV1a, 5},
{"日本", 10, CRC64, 6},
{"localhost", 10, NewCRC32, 9},
{"ёлка", 10, NewCRC64, 6},
{"ветер", 10, NewFNV1, 3},
{"中国", 10, NewFNV1a, 5},
{"日本", 10, NewCRC64, 6},
}

func TestJumpHashString(t *testing.T) {
for _, v := range jumpStringTestVectors {
h := HashString(v.key, v.buckets, v.hasher)
h := HashString(v.key, v.buckets, v.hasher())
if h != v.expected {
t.Errorf("expected bucket for key=%s to be %d, got %d",
strconv.Quote(v.key), v.expected, h)
Expand All @@ -57,7 +57,7 @@ func TestJumpHashString(t *testing.T) {

func TestHasher(t *testing.T) {
for _, v := range jumpStringTestVectors {
hasher := New(int(v.buckets), v.hasher)
hasher := New(int(v.buckets), v.hasher())
h := hasher.Hash(v.key)
if int32(h) != v.expected {
t.Errorf("expected bucket for key=%s to be %d, got %d",
Expand All @@ -72,7 +72,7 @@ func ExampleHash() {
}

func ExampleHashString() {
fmt.Print(HashString("127.0.0.1", 8, CRC64))
fmt.Print(HashString("127.0.0.1", 8, NewCRC64()))
// Output: 7
}

Expand All @@ -85,27 +85,27 @@ func BenchmarkHash(b *testing.B) {
func BenchmarkHashStringCRC32(b *testing.B) {
s := "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat."
for i := 0; i < b.N; i++ {
HashString(s, int32(i), CRC32)
HashString(s, int32(i), NewCRC32())
}
}

func BenchmarkHashStringCRC64(b *testing.B) {
s := "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat."
for i := 0; i < b.N; i++ {
HashString(s, int32(i), CRC64)
HashString(s, int32(i), NewCRC64())
}
}

func BenchmarkHashStringFNV1(b *testing.B) {
s := "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat."
for i := 0; i < b.N; i++ {
HashString(s, int32(i), FNV1)
HashString(s, int32(i), NewFNV1())
}
}

func BenchmarkHashStringFNV1a(b *testing.B) {
s := "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat."
for i := 0; i < b.N; i++ {
HashString(s, int32(i), FNV1a)
HashString(s, int32(i), NewFNV1a())
}
}

0 comments on commit fa2de10

Please sign in to comment.