Go xxHash Code Example (Online Runner)

Go xxHash examples for XXH32, XXH64, XXH3-64, and XXH3-128 with seeds and hex/decimal output.

Online calculator: use the site xxHash text tool.

Note: This snippet requires locally installed dependencies and will not run in the online runner. Run it locally with: go mod init xxhash-demo && go get github.com/zeebo/xxh3 && go run xxhash_basic.go

Calculation method

XXH32 uses a 32-bit seed, while XXH64/XXH3 use a 64-bit seed built from low/high 32-bit words. Output can be hex or decimal and is formatted to the correct width.

Implementation notes

  • Package: pure Go for XXH32/XXH64, plus github.com/zeebo/xxh3 for XXH3.
  • Implementation: seeds are combined from low/high 32-bit words to match the tool.
  • Notes: xxHash is not cryptographic.

Text hashing example

go
package main

import (
	"fmt"
	"strconv"

	"github.com/zeebo/xxh3"
)

func xxh3_64Text(text string) string {
	value := xxh3.Hash([]byte(text))
	return strconv.FormatUint(value, 10)
}

func main() {
	fmt.Println(xxh3_64Text("hello"))
}

File hashing example

go
package main

import (
	"fmt"
	"math/big"
	"os"

	"github.com/zeebo/xxh3"
)

func xxh3_128File(path string) (string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	value := xxh3.Hash128(data)
	bigValue := new(big.Int).SetUint64(value.Hi)
	bigValue.Lsh(bigValue, 64)
	bigValue.Or(bigValue, new(big.Int).SetUint64(value.Lo))
	return bigValue.Text(10), nil
}

func main() {
	file, err := os.CreateTemp("", "xxhash-example-*.bin")
	if err != nil {
		panic(err)
	}
	defer os.Remove(file.Name())
	file.WriteString("hello")
	file.Close()

	value, err := xxh3_128File(file.Name())
	if err != nil {
		panic(err)
	}
	fmt.Println(value)
}

Complete script (implementation + tests)

go
package main

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

	"github.com/zeebo/xxh3"
)

type Variant string

type OutputFormat string

const (
	VarXXH32  Variant = "xxhash32"
	VarXXH64  Variant = "xxhash64"
	VarXXH3_64 Variant = "xxh3_64"
	VarXXH3_128 Variant = "xxh3_128"
)

const (
	OutputHex    OutputFormat = "hex"
	OutputDecimal OutputFormat = "decimal"
)

const (
	prime32_1 uint32 = 2654435761
	prime32_2 uint32 = 2246822519
	prime32_3 uint32 = 3266489917
	prime32_4 uint32 = 668265263
	prime32_5 uint32 = 374761393

	prime64_1 uint64 = 11400714785074694791
	prime64_2 uint64 = 14029467366897019727
	prime64_3 uint64 = 1609587929392839161
	prime64_4 uint64 = 9650029242287828579
	prime64_5 uint64 = 2870177450012600261
)

func parseSeed32(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 combineSeed(low, high uint32) uint64 {
	return (uint64(high) << 32) | uint64(low)
}

func round32(acc, input uint32) uint32 {
	acc += input * prime32_2
	acc = bits.RotateLeft32(acc, 13)
	acc *= prime32_1
	return acc
}

func mergeRound32(acc, val uint32) uint32 {
	acc ^= round32(0, val)
	acc = acc*prime32_1 + prime32_4
	return acc
}

func xxh32(data []byte, seed uint32) uint32 {
	length := len(data)
	var h32 uint32
	idx := 0

	if length >= 16 {
		v1 := seed + prime32_1 + prime32_2
		v2 := seed + prime32_2
		v3 := seed
		v4 := seed - prime32_1

		for idx <= length-16 {
			v1 = round32(v1, binary.LittleEndian.Uint32(data[idx:]))
			v2 = round32(v2, binary.LittleEndian.Uint32(data[idx+4:]))
			v3 = round32(v3, binary.LittleEndian.Uint32(data[idx+8:]))
			v4 = round32(v4, binary.LittleEndian.Uint32(data[idx+12:]))
			idx += 16
		}

		h32 = bits.RotateLeft32(v1, 1) + bits.RotateLeft32(v2, 7) + bits.RotateLeft32(v3, 12) + bits.RotateLeft32(v4, 18)
		h32 = mergeRound32(h32, v1)
		h32 = mergeRound32(h32, v2)
		h32 = mergeRound32(h32, v3)
		h32 = mergeRound32(h32, v4)
	} else {
		h32 = seed + prime32_5
	}

	h32 += uint32(length)

	for idx <= length-4 {
		h32 += binary.LittleEndian.Uint32(data[idx:]) * prime32_3
		h32 = bits.RotateLeft32(h32, 17) * prime32_4
		idx += 4
	}

	for idx < length {
		h32 += uint32(data[idx]) * prime32_5
		h32 = bits.RotateLeft32(h32, 11) * prime32_1
		idx++
	}

	h32 ^= h32 >> 15
	h32 *= prime32_2
	h32 ^= h32 >> 13
	h32 *= prime32_3
	h32 ^= h32 >> 16
	return h32
}

func round64(acc, input uint64) uint64 {
	acc += input * prime64_2
	acc = bits.RotateLeft64(acc, 31)
	acc *= prime64_1
	return acc
}

func mergeRound64(acc, val uint64) uint64 {
	acc ^= round64(0, val)
	acc = acc*prime64_1 + prime64_4
	return acc
}

func xxh64(data []byte, seed uint64) uint64 {
	length := len(data)
	idx := 0
	var h64 uint64

	if length >= 32 {
		v1 := seed + prime64_1 + prime64_2
		v2 := seed + prime64_2
		v3 := seed
		v4 := seed - prime64_1

		for idx <= length-32 {
			v1 = round64(v1, binary.LittleEndian.Uint64(data[idx:]))
			v2 = round64(v2, binary.LittleEndian.Uint64(data[idx+8:]))
			v3 = round64(v3, binary.LittleEndian.Uint64(data[idx+16:]))
			v4 = round64(v4, binary.LittleEndian.Uint64(data[idx+24:]))
			idx += 32
		}

		h64 = bits.RotateLeft64(v1, 1) + bits.RotateLeft64(v2, 7) + bits.RotateLeft64(v3, 12) + bits.RotateLeft64(v4, 18)
		h64 = mergeRound64(h64, v1)
		h64 = mergeRound64(h64, v2)
		h64 = mergeRound64(h64, v3)
		h64 = mergeRound64(h64, v4)
	} else {
		h64 = seed + prime64_5
	}

	h64 += uint64(length)

	for idx <= length-8 {
		k1 := round64(0, binary.LittleEndian.Uint64(data[idx:]))
		h64 ^= k1
		h64 = bits.RotateLeft64(h64, 27)*prime64_1 + prime64_4
		idx += 8
	}

	if idx <= length-4 {
		h64 ^= uint64(binary.LittleEndian.Uint32(data[idx:])) * prime64_1
		h64 = bits.RotateLeft64(h64, 23)*prime64_2 + prime64_3
		idx += 4
	}

	for idx < length {
		h64 ^= uint64(data[idx]) * prime64_5
		h64 = bits.RotateLeft64(h64, 11) * prime64_1
		idx++
	}

	h64 ^= h64 >> 33
	h64 *= prime64_2
	h64 ^= h64 >> 29
	h64 *= prime64_3
	h64 ^= h64 >> 32
	return h64
}

func formatOutput(value *big.Int, bits int, output OutputFormat) string {
	if output == OutputDecimal {
		return value.Text(10)
	}
	return fmt.Sprintf("%0*x", bits/4, value)
}

func xxhashText(text string, variant Variant, seed32 uint32, seedLow uint32, seedHigh uint32, output OutputFormat) string {
	data := []byte(text)
	switch variant {
	case VarXXH32:
		value := xxh32(data, seed32)
		return formatOutput(new(big.Int).SetUint64(uint64(value)), 32, output)
	case VarXXH64:
		value := xxh64(data, combineSeed(seedLow, seedHigh))
		return formatOutput(new(big.Int).SetUint64(value), 64, output)
	case VarXXH3_64:
		value := xxh3.HashSeed(data, combineSeed(seedLow, seedHigh))
		return formatOutput(new(big.Int).SetUint64(value), 64, output)
	case VarXXH3_128:
		value := xxh3.Hash128Seed(data, combineSeed(seedLow, seedHigh))
		bigValue := new(big.Int).SetUint64(value.Hi)
		bigValue.Lsh(bigValue, 64)
		bigValue.Or(bigValue, new(big.Int).SetUint64(value.Lo))
		return formatOutput(bigValue, 128, output)
	default:
		return ""
	}
}

func main() {
	seed32 := parseSeed32("1234")
	seedLow := parseSeed32("0x1234")
	seedHigh := parseSeed32("0xabcd")

	fmt.Println(xxhashText("hello", VarXXH32, seed32, 0, 0, OutputHex))
	fmt.Println(xxhashText("hello", VarXXH3_128, 0, seedLow, seedHigh, OutputDecimal))
}