Go MurmurHash2 Code Example (Online Runner)
Go MurmurHash2 examples with variant selection, seeds, and number/hex output matching the MurmurHash2 tools.
Online calculator: use the site MurmurHash2 text tool.
Calculation method
MurmurHash2 supports multiple variants (32-bit x86, 64-bit x64/x86, and the Merkle-Damgard flavor). This helper mirrors those variants and output formats.
Implementation notes
- Package: no external dependencies; this is a pure Go implementation.
- Implementation: seeds are parsed as 32-bit values (decimal or hex) to match the tool.
- Notes: MurmurHash2 is not cryptographic; use it only for non-security checksums.
Text hashing example
go
package main
import (
"encoding/binary"
"fmt"
)
func murmur2X86_32(data []byte, seed uint32) uint32 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
length := len(data)
h := seed ^ uint32(length)
i := 0
for length >= 4 {
k := binary.LittleEndian.Uint32(data[i:])
k *= m
k ^= k >> r
k *= m
h *= m
h ^= k
i += 4
length -= 4
}
switch length {
case 3:
h ^= uint32(data[i+2]) << 16
fallthrough
case 2:
h ^= uint32(data[i+1]) << 8
fallthrough
case 1:
h ^= uint32(data[i])
h *= m
}
h ^= h >> 13
h *= m
h ^= h >> 15
return h
}
func main() {
fmt.Printf("%08x\n", murmur2X86_32([]byte("hello"), 0))
}File hashing example
go
package main
import (
"encoding/binary"
"fmt"
"os"
)
func murmur2X86_32(data []byte, seed uint32) uint32 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
length := len(data)
h := seed ^ uint32(length)
i := 0
for length >= 4 {
k := binary.LittleEndian.Uint32(data[i:])
k *= m
k ^= k >> r
k *= m
h *= m
h ^= k
i += 4
length -= 4
}
switch length {
case 3:
h ^= uint32(data[i+2]) << 16
fallthrough
case 2:
h ^= uint32(data[i+1]) << 8
fallthrough
case 1:
h ^= uint32(data[i])
h *= m
}
h ^= h >> 13
h *= m
h ^= h >> 15
return h
}
func main() {
file, err := os.CreateTemp("", "murmur2-example-*.bin")
if err != nil {
panic(err)
}
defer os.Remove(file.Name())
file.WriteString("hello")
file.Close()
data, err := os.ReadFile(file.Name())
if err != nil {
panic(err)
}
fmt.Printf("%08x\n", murmur2X86_32(data, 0))
}Complete script (implementation + tests)
go
package main
import (
"encoding/binary"
"fmt"
"strconv"
"strings"
)
type Variant string
type OutputFormat string
const (
VarX86_32 Variant = "x86_32"
VarX86_32A Variant = "x86_32a"
VarNeutral32 Variant = "neutral_32"
VarAligned32 Variant = "aligned_32"
VarX64_64A Variant = "x64_64a"
VarX86_64B Variant = "x86_64b"
)
const (
OutputNumber OutputFormat = "number"
OutputHex OutputFormat = "hex"
)
func parseSeed(value string) uint32 {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return 0
}
parsed, err := strconv.ParseUint(trimmed, 0, 32)
if err != nil {
return 0
}
return uint32(parsed)
}
func murmur2X86_32(data []byte, seed uint32) uint32 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
length := len(data)
h := seed ^ uint32(length)
i := 0
for length >= 4 {
k := binary.LittleEndian.Uint32(data[i:])
k *= m
k ^= k >> r
k *= m
h *= m
h ^= k
i += 4
length -= 4
}
switch length {
case 3:
h ^= uint32(data[i+2]) << 16
fallthrough
case 2:
h ^= uint32(data[i+1]) << 8
fallthrough
case 1:
h ^= uint32(data[i])
h *= m
}
h ^= h >> 13
h *= m
h ^= h >> 15
return h
}
func mmix(h, k uint32) uint32 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
k *= m
k ^= k >> r
k *= m
h *= m
h ^= k
return h
}
func murmur2A(data []byte, seed uint32) uint32 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
length := len(data)
h := seed
i := 0
remaining := length
for remaining >= 4 {
k := binary.LittleEndian.Uint32(data[i:])
h = mmix(h, k)
i += 4
remaining -= 4
}
var t uint32
switch remaining {
case 3:
t ^= uint32(data[i+2]) << 16
fallthrough
case 2:
t ^= uint32(data[i+1]) << 8
fallthrough
case 1:
t ^= uint32(data[i])
}
h = mmix(h, t)
h = mmix(h, uint32(length))
h ^= h >> 13
h *= m
h ^= h >> 15
return h
}
func murmur64A(data []byte, seed uint32) uint64 {
const m uint64 = 0xC6A4A7935BD1E995
const r uint = 47
h := uint64(seed) ^ (uint64(len(data)) * m)
i := 0
for i+8 <= len(data) {
k := binary.LittleEndian.Uint64(data[i:])
k *= m
k ^= k >> r
k *= m
h ^= k
h *= m
i += 8
}
remaining := data[i:]
switch len(remaining) {
case 7:
h ^= uint64(remaining[6]) << 48
fallthrough
case 6:
h ^= uint64(remaining[5]) << 40
fallthrough
case 5:
h ^= uint64(remaining[4]) << 32
fallthrough
case 4:
h ^= uint64(remaining[3]) << 24
fallthrough
case 3:
h ^= uint64(remaining[2]) << 16
fallthrough
case 2:
h ^= uint64(remaining[1]) << 8
fallthrough
case 1:
h ^= uint64(remaining[0])
h *= m
}
h ^= h >> r
h *= m
h ^= h >> r
return h
}
func murmur64B(data []byte, seed uint32) uint64 {
const m uint32 = 0x5BD1E995
const r uint32 = 24
length := len(data)
i := 0
h1 := seed ^ uint32(length)
h2 := uint32(seed >> 32)
for length >= 8 {
k1 := binary.LittleEndian.Uint32(data[i:])
i += 4
length -= 4
k1 *= m
k1 ^= k1 >> r
k1 *= m
h1 *= m
h1 ^= k1
k2 := binary.LittleEndian.Uint32(data[i:])
i += 4
length -= 4
k2 *= m
k2 ^= k2 >> r
k2 *= m
h2 *= m
h2 ^= k2
}
if length >= 4 {
k1 := binary.LittleEndian.Uint32(data[i:])
i += 4
length -= 4
k1 *= m
k1 ^= k1 >> r
k1 *= m
h1 *= m
h1 ^= k1
}
if length > 0 {
switch length {
case 3:
h2 ^= uint32(data[i+2]) << 16
fallthrough
case 2:
h2 ^= uint32(data[i+1]) << 8
fallthrough
case 1:
h2 ^= uint32(data[i])
h2 *= m
}
}
h1 ^= h2 >> 18
h1 *= m
h2 ^= h1 >> 22
h2 *= m
h1 ^= h2 >> 17
h1 *= m
h2 ^= h1 >> 19
h2 *= m
return (uint64(h1) << 32) | uint64(h2)
}
func murmur2Hash(text string, variant Variant, seed uint32, output OutputFormat) string {
data := []byte(text)
switch variant {
case VarX86_32:
value := murmur2X86_32(data, seed)
if output == OutputNumber {
return strconv.FormatUint(uint64(value), 10)
}
return fmt.Sprintf("%08x", value)
case VarX86_32A:
value := murmur2A(data, seed)
if output == OutputNumber {
return strconv.FormatUint(uint64(value), 10)
}
return fmt.Sprintf("%08x", value)
case VarNeutral32, VarAligned32:
value := murmur2X86_32(data, seed)
if output == OutputNumber {
return strconv.FormatUint(uint64(value), 10)
}
return fmt.Sprintf("%08x", value)
case VarX64_64A:
value := murmur64A(data, seed)
if output == OutputNumber {
return strconv.FormatUint(value, 10)
}
return fmt.Sprintf("%016x", value)
case VarX86_64B:
value := murmur64B(data, seed)
if output == OutputNumber {
return strconv.FormatUint(value, 10)
}
return fmt.Sprintf("%016x", value)
default:
return ""
}
}
func main() {
seed := parseSeed("0x2a")
fmt.Println(murmur2Hash("hello", VarX86_32, seed, OutputNumber))
fmt.Println(murmur2Hash("hello", VarX64_64A, seed, OutputHex))
if murmur2Hash("", VarX86_32, 0, OutputHex) != "00000000" {
panic("Murmur2 empty mismatch")
}
if murmur2Hash("hello", VarX86_32, 0, OutputHex) != "e56129cb" {
panic("Murmur2 x86_32 mismatch")
}
if murmur2Hash("hello", VarX64_64A, 0, OutputHex) != "1e68d17c457bf117" {
panic("Murmur2 x64_64a mismatch")
}
fmt.Println("MurmurHash2 tests passed")
}