diff --git a/murmur.go b/murmur.go index f99557c..1252cf7 100644 --- a/murmur.go +++ b/murmur.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. /* -Native (and fast) implementation of Austin Appleby's MurmurHash3. - Package murmur3 implements Austin Appleby's non-cryptographic MurmurHash3. Reference implementation: @@ -26,6 +24,7 @@ type digest struct { clen int // Digested input cumulative length. tail []byte // 0 to Size()-1 bytes view of `buf'. buf [16]byte // Expected (but not required) to be Size() large. + seed uint32 // Seed for initializing the hash. bmixer } diff --git a/murmur128.go b/murmur128.go index 568eba7..a4b618b 100644 --- a/murmur128.go +++ b/murmur128.go @@ -18,6 +18,7 @@ var ( _ bmixer = new(digest128) ) +// Hash128 represents a 128-bit hasher // Hack: the standard api doesn't define any Hash128 interface. type Hash128 interface { hash.Hash @@ -27,15 +28,17 @@ type Hash128 interface { // digest128 represents a partial evaluation of a 128 bites hash. type digest128 struct { digest - seed uint64 - h1 uint64 // Unfinalized running hash part 1. - h2 uint64 // Unfinalized running hash part 2. + h1 uint64 // Unfinalized running hash part 1. + h2 uint64 // Unfinalized running hash part 2. } +// New128 returns a 128-bit hasher func New128() Hash128 { return New128WithSeed(0) } +// New128WithSeed returns a 128-bit hasher set with explicit seed value func New128WithSeed(seed uint32) Hash128 { - d := &digest128{seed: uint64(seed)} + d := new(digest128) + d.seed = seed d.bmixer = d d.Reset() return d @@ -43,7 +46,7 @@ func New128WithSeed(seed uint32) Hash128 { func (d *digest128) Size() int { return 16 } -func (d *digest128) reset() { d.h1, d.h2 = d.seed, d.seed } +func (d *digest128) reset() { d.h1, d.h2 = uint64(d.seed), uint64(d.seed) } func (d *digest128) Sum(b []byte) []byte { h1, h2 := d.Sum128() @@ -186,8 +189,14 @@ func rotl64(x uint64, r byte) uint64 { // return hasher.Sum128() func Sum128(data []byte) (h1 uint64, h2 uint64) { return Sum128WithSeed(data, 0) } +// Sum128WithSeed returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New128WithSeed(seed) +// hasher.Write(data) +// return hasher.Sum128() func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) { d := &digest128{h1: uint64(seed), h2: uint64(seed)} + d.seed = seed d.tail = d.bmix(data) d.clen = len(data) return d.Sum128() diff --git a/murmur32.go b/murmur32.go index 7f687ea..6f9ed20 100644 --- a/murmur32.go +++ b/murmur32.go @@ -11,7 +11,6 @@ import ( var ( _ hash.Hash = new(digest32) _ hash.Hash32 = new(digest32) - _ bmixer = new(digest32) ) const ( @@ -22,14 +21,16 @@ const ( // digest32 represents a partial evaluation of a 32 bites hash. type digest32 struct { digest - seed uint32 - h1 uint32 // Unfinalized running hash. + h1 uint32 // Unfinalized running hash. } +// New32 returns new 32-bit hasher func New32() hash.Hash32 { return New32WithSeed(0) } +// New32WithSeed returns new 32-bit hasher set with explicit seed value func New32WithSeed(seed uint32) hash.Hash32 { - d := &digest32{seed: seed} + d := new(digest32) + d.seed = seed d.bmixer = d d.Reset() return d @@ -108,9 +109,14 @@ func rotl32(x uint32, r byte) uint32 { // return hasher.Sum32() func Sum32(data []byte) uint32 { return Sum32WithSeed(data, 0) } +// Sum32WithSeed returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New32WithSeed(seed) +// hasher.Write(data) +// return hasher.Sum32() func Sum32WithSeed(data []byte, seed uint32) uint32 { - var h1 uint32 = seed + h1 := seed nblocks := len(data) / 4 var p uintptr diff --git a/murmur64.go b/murmur64.go index 85580d5..65a410a 100644 --- a/murmur64.go +++ b/murmur64.go @@ -14,8 +14,10 @@ var ( // digest64 is half a digest128. type digest64 digest128 +// New64 returns a 64-bit hasher func New64() hash.Hash64 { return New64WithSeed(0) } +// New64WithSeed returns a 64-bit hasher set with explicit seed value func New64WithSeed(seed uint32) hash.Hash64 { d := (*digest64)(New128WithSeed(seed).(*digest128)) return d @@ -40,8 +42,14 @@ func (d *digest64) Sum64() uint64 { // return hasher.Sum64() func Sum64(data []byte) uint64 { return Sum64WithSeed(data, 0) } +// Sum64WithSeed returns the MurmurHash3 sum of data. It is equivalent to the +// following sequence (without the extra burden and the extra allocation): +// hasher := New64WithSeed(seed) +// hasher.Write(data) +// return hasher.Sum64() func Sum64WithSeed(data []byte, seed uint32) uint64 { d := &digest128{h1: uint64(seed), h2: uint64(seed)} + d.seed = seed d.tail = d.bmix(data) d.clen = len(data) h1, _ := d.Sum128() diff --git a/murmur_test.go b/murmur_test.go index 45367bd..fe564d0 100644 --- a/murmur_test.go +++ b/murmur_test.go @@ -2,7 +2,6 @@ package murmur3 import ( "fmt" - "hash" "testing" ) @@ -35,55 +34,56 @@ var data = []struct { func TestRefStrings(t *testing.T) { for _, elem := range data { - var h32 hash.Hash32 = New32WithSeed(elem.seed) + h32 := New32WithSeed(elem.seed) h32.Write([]byte(elem.s)) if v := h32.Sum32(); v != elem.h32 { - t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32) + t.Errorf("[Hash32] key: '%s', seed: '%d': 0x%x (want 0x%x)", elem.s, elem.seed, v, elem.h32) } h32.Reset() h32.Write([]byte(elem.s)) target := fmt.Sprintf("%08x", elem.h32) if p := fmt.Sprintf("%x", h32.Sum(nil)); p != target { - t.Errorf("'%s': %s (want %s)", elem.s, p, target) + t.Errorf("[Hash32] key: '%s', seed: '%d': %s (want %s)", elem.s, elem.seed, p, target) } if v := Sum32WithSeed([]byte(elem.s), elem.seed); v != elem.h32 { - t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32) + t.Errorf("[Hash32] key '%s', seed: '%d': 0x%x (want 0x%x)", elem.s, elem.seed, v, elem.h32) } - var h64 hash.Hash64 = New64WithSeed(elem.seed) + h64 := New64WithSeed(elem.seed) h64.Write([]byte(elem.s)) if v := h64.Sum64(); v != elem.h64_1 { - t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1) + t.Errorf("'[Hash64] key: '%s', seed: '%d': 0x%x (want 0x%x)", elem.s, elem.seed, v, elem.h64_1) } h64.Reset() h64.Write([]byte(elem.s)) target = fmt.Sprintf("%016x", elem.h64_1) if p := fmt.Sprintf("%x", h64.Sum(nil)); p != target { - t.Errorf("'%s': %s (want %s)", elem.s, p, target) + t.Errorf("[Hash64] key: '%s', seed: '%d': %s (want %s)", elem.s, elem.seed, p, target) } if v := Sum64WithSeed([]byte(elem.s), elem.seed); v != elem.h64_1 { - t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h64_1) + t.Errorf("[Hash64] key: '%s', seed: '%d': 0x%x (want 0x%x)", elem.s, elem.seed, v, elem.h64_1) } - var h128 Hash128 = New128WithSeed(elem.seed) + h128 := New128WithSeed(elem.seed) + h128.Write([]byte(elem.s)) if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 { - t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2) + t.Errorf("[Hash128] key: '%s', seed: '%d': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, elem.seed, v1, v2, elem.h64_1, elem.h64_2) } h128.Reset() h128.Write([]byte(elem.s)) target = fmt.Sprintf("%016x%016x", elem.h64_1, elem.h64_2) if p := fmt.Sprintf("%x", h128.Sum(nil)); p != target { - t.Errorf("'%s': %s (want %s)", elem.s, p, target) + t.Errorf("[Hash128] key: '%s', seed: '%d': %s (want %s)", elem.s, elem.seed, p, target) } if v1, v2 := Sum128WithSeed([]byte(elem.s), elem.seed); v1 != elem.h64_1 || v2 != elem.h64_2 { - t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2) + t.Errorf("[Hash128] key: '%s', seed: '%d': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, elem.seed, v1, v2, elem.h64_1, elem.h64_2) } } } @@ -92,7 +92,8 @@ func TestIncremental(t *testing.T) { for _, elem := range data { h32 := New32WithSeed(elem.seed) h128 := New128WithSeed(elem.seed) - for i, j, k := 0, 0, len(elem.s); i < k; i = j { + var i, j int + for k := len(elem.s); i < k; i = j { j = 2*i + 3 if j > k { j = k @@ -104,10 +105,10 @@ func TestIncremental(t *testing.T) { } println() if v := h32.Sum32(); v != elem.h32 { - t.Errorf("'%s': 0x%x (want 0x%x)", elem.s, v, elem.h32) + t.Errorf("[Hash32] key: '%s', seed: '%d': 0x%x (want 0x%x)", elem.s, elem.seed, v, elem.h32) } if v1, v2 := h128.Sum128(); v1 != elem.h64_1 || v2 != elem.h64_2 { - t.Errorf("'%s': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, v1, v2, elem.h64_1, elem.h64_2) + t.Errorf("[Hash128] key: '%s', seed: '%d': 0x%x-0x%x (want 0x%x-0x%x)", elem.s, elem.seed, v1, v2, elem.h64_1, elem.h64_2) } } }