Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to parameterize seed value as in original murmur3 implementation #15

Merged
merged 1 commit into from
Aug 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions murmur.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
}

Expand Down
19 changes: 14 additions & 5 deletions murmur128.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,23 +28,25 @@ 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
}

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()
Expand Down Expand Up @@ -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()
Expand Down
16 changes: 11 additions & 5 deletions murmur32.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
var (
_ hash.Hash = new(digest32)
_ hash.Hash32 = new(digest32)
_ bmixer = new(digest32)
)

const (
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions murmur64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down
33 changes: 17 additions & 16 deletions murmur_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package murmur3

import (
"fmt"
"hash"
"testing"
)

Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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
Expand All @@ -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)
}
}
}
Expand Down