Go MurmurHash3 Code Example (Online Runner)

Go MurmurHash3 examples with 32-bit/128-bit variants, seeds, and number/hex output to match the tools.

Online calculator: use the site MurmurHash3 text tool.

Calculation method

MurmurHash3 supports 32-bit (x86) and 128-bit (x86/x64) variants. This helper mirrors the tool’s variant and output format options.

Implementation notes

  • Package: no external dependencies; this is a pure Go implementation.
  • Implementation: outputs are masked to unsigned values and formatted as decimal or hex.
  • Notes: MurmurHash3 is non-cryptographic; keep the seed consistent across systems.

Text hashing example

go
package main

import (
	"encoding/binary"
	"fmt"
	"math/bits"
)

func murmur3X86_32(data []byte, seed uint32) uint32 {
	const (
		c1 uint32 = 0xcc9e2d51
		c2 uint32 = 0x1b873593
	)
	h1 := seed
	nblocks := len(data) / 4
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(data[i*4:])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
		h1 = bits.RotateLeft32(h1, 13)
		h1 = h1*5 + 0xe6546b64
	}

	var k1 uint32
	tail := data[nblocks*4:]
	switch len(tail) {
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	h1 ^= uint32(len(data))
	h1 ^= h1 >> 16
	h1 *= 0x85ebca6b
	h1 ^= h1 >> 13
	h1 *= 0xc2b2ae35
	h1 ^= h1 >> 16
	return h1
}

func main() {
	value := murmur3X86_32([]byte("hello"), 0)
	fmt.Println(value)
}

File hashing example

go
package main

import (
	"encoding/binary"
	"fmt"
	"math/bits"
	"os"
)

func murmur3X86_32(data []byte, seed uint32) uint32 {
	const (
		c1 uint32 = 0xcc9e2d51
		c2 uint32 = 0x1b873593
	)
	h1 := seed
	nblocks := len(data) / 4
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(data[i*4:])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
		h1 = bits.RotateLeft32(h1, 13)
		h1 = h1*5 + 0xe6546b64
	}

	var k1 uint32
	tail := data[nblocks*4:]
	switch len(tail) {
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	h1 ^= uint32(len(data))
	h1 ^= h1 >> 16
	h1 *= 0x85ebca6b
	h1 ^= h1 >> 13
	h1 *= 0xc2b2ae35
	h1 ^= h1 >> 16
	return h1
}

func main() {
	file, err := os.CreateTemp("", "murmur3-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.Println(murmur3X86_32(data, 0))
}

Complete script (implementation + tests)

go
package main

import (
	"encoding/binary"
	"fmt"
	"math/big"
	"math/bits"
	"strconv"
	"strings"
)

type OutputFormat string

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 fmix32(h uint32) uint32 {
	h ^= h >> 16
	h *= 0x85ebca6b
	h ^= h >> 13
	h *= 0xc2b2ae35
	h ^= h >> 16
	return h
}

func fmix64(k uint64) uint64 {
	k ^= k >> 33
	k *= 0xff51afd7ed558ccd
	k ^= k >> 33
	k *= 0xc4ceb9fe1a85ec53
	k ^= k >> 33
	return k
}

func murmur3X86_32(data []byte, seed uint32) uint32 {
	const (
		c1 uint32 = 0xcc9e2d51
		c2 uint32 = 0x1b873593
	)
	h1 := seed
	nblocks := len(data) / 4
	for i := 0; i < nblocks; i++ {
		k1 := binary.LittleEndian.Uint32(data[i*4:])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
		h1 = bits.RotateLeft32(h1, 13)
		h1 = h1*5 + 0xe6546b64
	}

	var k1 uint32
	tail := data[nblocks*4:]
	switch len(tail) {
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	h1 ^= uint32(len(data))
	h1 = fmix32(h1)
	return h1
}

func murmur3X86_128(data []byte, seed uint32) (uint32, uint32, uint32, uint32) {
	const (
		c1 uint32 = 0x239b961b
		c2 uint32 = 0xab0e9789
		c3 uint32 = 0x38b34ae5
		c4 uint32 = 0xa1e38b93
	)
	var h1, h2, h3, h4 uint32 = seed, seed, seed, seed
	nblocks := len(data) / 16
	for i := 0; i < nblocks; i++ {
		block := data[i*16:]
		k1 := binary.LittleEndian.Uint32(block[0:4])
		k2 := binary.LittleEndian.Uint32(block[4:8])
		k3 := binary.LittleEndian.Uint32(block[8:12])
		k4 := binary.LittleEndian.Uint32(block[12:16])

		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
		h1 = bits.RotateLeft32(h1, 19)
		h1 += h2
		h1 = h1*5 + 0x561ccd1b

		k2 *= c2
		k2 = bits.RotateLeft32(k2, 16)
		k2 *= c3
		h2 ^= k2
		h2 = bits.RotateLeft32(h2, 17)
		h2 += h3
		h2 = h2*5 + 0x0bcaa747

		k3 *= c3
		k3 = bits.RotateLeft32(k3, 17)
		k3 *= c4
		h3 ^= k3
		h3 = bits.RotateLeft32(h3, 15)
		h3 += h4
		h3 = h3*5 + 0x96cd1c35

		k4 *= c4
		k4 = bits.RotateLeft32(k4, 18)
		k4 *= c1
		h4 ^= k4
		h4 = bits.RotateLeft32(h4, 13)
		h4 += h1
		h4 = h4*5 + 0x32ac3b17
	}

	tail := data[nblocks*16:]
	var k1, k2, k3, k4 uint32
	switch len(tail) & 15 {
	case 15:
		k4 ^= uint32(tail[14]) << 16
		fallthrough
	case 14:
		k4 ^= uint32(tail[13]) << 8
		fallthrough
	case 13:
		k4 ^= uint32(tail[12])
		k4 *= c4
		k4 = bits.RotateLeft32(k4, 18)
		k4 *= c1
		h4 ^= k4
		fallthrough
	case 12:
		k3 ^= uint32(tail[11]) << 24
		fallthrough
	case 11:
		k3 ^= uint32(tail[10]) << 16
		fallthrough
	case 10:
		k3 ^= uint32(tail[9]) << 8
		fallthrough
	case 9:
		k3 ^= uint32(tail[8])
		k3 *= c3
		k3 = bits.RotateLeft32(k3, 17)
		k3 *= c4
		h3 ^= k3
		fallthrough
	case 8:
		k2 ^= uint32(tail[7]) << 24
		fallthrough
	case 7:
		k2 ^= uint32(tail[6]) << 16
		fallthrough
	case 6:
		k2 ^= uint32(tail[5]) << 8
		fallthrough
	case 5:
		k2 ^= uint32(tail[4])
		k2 *= c2
		k2 = bits.RotateLeft32(k2, 16)
		k2 *= c3
		h2 ^= k2
		fallthrough
	case 4:
		k1 ^= uint32(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint32(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint32(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint32(tail[0])
		k1 *= c1
		k1 = bits.RotateLeft32(k1, 15)
		k1 *= c2
		h1 ^= k1
	}

	length := uint32(len(data))
	h1 ^= length
	h2 ^= length
	h3 ^= length
	h4 ^= length

	h1 += h2 + h3 + h4
	h2 += h1
	h3 += h1
	h4 += h1

	h1 = fmix32(h1)
	h2 = fmix32(h2)
	h3 = fmix32(h3)
	h4 = fmix32(h4)

	h1 += h2 + h3 + h4
	h2 += h1
	h3 += h1
	h4 += h1

	return h1, h2, h3, h4
}

func murmur3X64_128(data []byte, seed uint32) (uint64, uint64) {
	const (
		c1 uint64 = 0x87c37b91114253d5
		c2 uint64 = 0x4cf5ad432745937f
	)
	var h1, h2 uint64 = uint64(seed), uint64(seed)
	nblocks := len(data) / 16
	for i := 0; i < nblocks; i++ {
		block := data[i*16:]
		k1 := binary.LittleEndian.Uint64(block[0:8])
		k2 := binary.LittleEndian.Uint64(block[8:16])

		k1 *= c1
		k1 = bits.RotateLeft64(k1, 31)
		k1 *= c2
		h1 ^= k1

		h1 = bits.RotateLeft64(h1, 27)
		h1 += h2
		h1 = h1*5 + 0x52dce729

		k2 *= c2
		k2 = bits.RotateLeft64(k2, 33)
		k2 *= c1
		h2 ^= k2

		h2 = bits.RotateLeft64(h2, 31)
		h2 += h1
		h2 = h2*5 + 0x38495ab5
	}

	tail := data[nblocks*16:]
	var k1, k2 uint64
	switch len(tail) & 15 {
	case 15:
		k2 ^= uint64(tail[14]) << 48
		fallthrough
	case 14:
		k2 ^= uint64(tail[13]) << 40
		fallthrough
	case 13:
		k2 ^= uint64(tail[12]) << 32
		fallthrough
	case 12:
		k2 ^= uint64(tail[11]) << 24
		fallthrough
	case 11:
		k2 ^= uint64(tail[10]) << 16
		fallthrough
	case 10:
		k2 ^= uint64(tail[9]) << 8
		fallthrough
	case 9:
		k2 ^= uint64(tail[8])
		k2 *= c2
		k2 = bits.RotateLeft64(k2, 33)
		k2 *= c1
		h2 ^= k2
		fallthrough
	case 8:
		k1 ^= uint64(tail[7]) << 56
		fallthrough
	case 7:
		k1 ^= uint64(tail[6]) << 48
		fallthrough
	case 6:
		k1 ^= uint64(tail[5]) << 40
		fallthrough
	case 5:
		k1 ^= uint64(tail[4]) << 32
		fallthrough
	case 4:
		k1 ^= uint64(tail[3]) << 24
		fallthrough
	case 3:
		k1 ^= uint64(tail[2]) << 16
		fallthrough
	case 2:
		k1 ^= uint64(tail[1]) << 8
		fallthrough
	case 1:
		k1 ^= uint64(tail[0])
		k1 *= c1
		k1 = bits.RotateLeft64(k1, 31)
		k1 *= c2
		h1 ^= k1
	}

	length := uint64(len(data))
	h1 ^= length
	h2 ^= length

	h1 += h2
	h2 += h1

	h1 = fmix64(h1)
	h2 = fmix64(h2)

	h1 += h2
	h2 += h1

	return h1, h2
}

func pack128Little64(low, high uint64) *big.Int {
	result := new(big.Int).SetUint64(high)
	result.Lsh(result, 64)
	result.Or(result, new(big.Int).SetUint64(low))
	return result
}

func pack128Little32(h1, h2, h3, h4 uint32) *big.Int {
	low := uint64(h1) | (uint64(h2) << 32)
	high := uint64(h3) | (uint64(h4) << 32)
	return pack128Little64(low, high)
}

func format128(value *big.Int, output OutputFormat) string {
	if output == OutputNumber {
		return value.Text(10)
	}
	return fmt.Sprintf("%032x", value)
}

func murmur3Hash(text string, bitLength int, arch string, seed uint32, output OutputFormat) string {
	data := []byte(text)
	if bitLength == 32 {
		value := murmur3X86_32(data, seed)
		if output == OutputNumber {
			return strconv.FormatUint(uint64(value), 10)
		}
		return fmt.Sprintf("%08x", value)
	}
	if arch == "x64" {
		h1, h2 := murmur3X64_128(data, seed)
		return format128(pack128Little64(h1, h2), output)
	}
	h1, h2, h3, h4 := murmur3X86_128(data, seed)
	return format128(pack128Little32(h1, h2, h3, h4), output)
}

func main() {
	seed := parseSeed("0xdeadbeef")
	fmt.Println(murmur3Hash("hello", 32, "x86", seed, OutputNumber))
	fmt.Println(murmur3Hash("hello", 128, "x64", seed, OutputHex))

	if murmur3Hash("hello", 32, "x86", 0, OutputNumber) != "613153351" {
		panic("Murmur3 x86_32 mismatch")
	}
	if murmur3Hash("hello", 128, "x64", 0, OutputNumber) != "121118445609844952839898260755277781762" {
		panic("Murmur3 x64_128 mismatch")
	}
	fmt.Println("MurmurHash3 tests passed")
}