Python MurmurHash2 Code Example (Online Runner)
Python 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). The tool lets you pick the variant and output format. This helper mirrors those options and uses the same 32-bit seed.
Implementation notes
- Package: no external dependency; the implementation is pure Python.
- Implementation: variants are selected explicitly and the seed is parsed as 32-bit. Output is formatted as decimal or hex.
- Notes: MurmurHash2 is not cryptographic. Different variants are not interchangeable; be consistent when matching other implementations.
python
from pathlib import Path
from typing import Literal
import tempfile
Variant = Literal[
"x86_32",
"x86_32a",
"neutral_32",
"aligned_32",
"x64_64a",
"x86_64b",
]
OutputFormat = Literal["number", "hex"]
UINT32_MASK = 0xFFFFFFFF
UINT64_MASK = (1 << 64) - 1
def parse_seed(value: str) -> int:
if not value.strip():
return 0
cleaned = value.strip()
base = 16 if cleaned.startswith(("0x", "0X")) else 10
return int(cleaned, base) & UINT32_MASK
def _mul32(a: int, b: int) -> int:
return (a * b) & UINT32_MASK
def _read_u32_le(data: bytes, offset: int) -> int:
return int.from_bytes(data[offset : offset + 4], "little")
def _read_u64_le(data: bytes, offset: int) -> int:
return int.from_bytes(data[offset : offset + 8], "little") & UINT64_MASK
def _murmur2_x86_32(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
h = (seed ^ length) & UINT32_MASK
index = 0
while length >= 4:
k = _read_u32_le(data, index)
k = _mul32(k, m)
k ^= (k >> r) & UINT32_MASK
k = _mul32(k, m)
h = _mul32(h, m)
h ^= k
index += 4
length -= 4
if length == 3:
h ^= data[index + 2] << 16
if length >= 2:
h ^= data[index + 1] << 8
if length >= 1:
h ^= data[index]
h = _mul32(h, m)
h ^= (h >> 13) & UINT32_MASK
h = _mul32(h, m)
h ^= (h >> 15) & UINT32_MASK
return h & UINT32_MASK
def _mmix(h: int, k: int) -> int:
m = 0x5BD1E995
r = 24
k = _mul32(k, m)
k ^= (k >> r) & UINT32_MASK
k = _mul32(k, m)
h = _mul32(h, m)
h ^= k
return h & UINT32_MASK
def _murmur2a(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
h = seed & UINT32_MASK
index = 0
remaining = length
while remaining >= 4:
k = _read_u32_le(data, index)
h = _mmix(h, k)
index += 4
remaining -= 4
t = 0
if remaining == 3:
t ^= data[index + 2] << 16
if remaining >= 2:
t ^= data[index + 1] << 8
if remaining >= 1:
t ^= data[index]
h = _mmix(h, t)
h = _mmix(h, length)
h ^= (h >> 13) & UINT32_MASK
h = _mul32(h, m)
h ^= (h >> 15) & UINT32_MASK
return h & UINT32_MASK
def _murmur64a(data: bytes, seed: int) -> int:
m = 0xC6A4A7935BD1E995
r = 47
h = (seed ^ (len(data) * m)) & UINT64_MASK
index = 0
while index + 8 <= len(data):
k = _read_u64_le(data, index)
k = (k * m) & UINT64_MASK
k ^= (k >> r)
k = (k * m) & UINT64_MASK
h ^= k
h = (h * m) & UINT64_MASK
index += 8
remaining = len(data) - index
if remaining >= 7:
h ^= data[index + 6] << 48
if remaining >= 6:
h ^= data[index + 5] << 40
if remaining >= 5:
h ^= data[index + 4] << 32
if remaining >= 4:
h ^= data[index + 3] << 24
if remaining >= 3:
h ^= data[index + 2] << 16
if remaining >= 2:
h ^= data[index + 1] << 8
if remaining >= 1:
h ^= data[index]
h = (h * m) & UINT64_MASK
h ^= h >> r
h = (h * m) & UINT64_MASK
h ^= h >> r
return h & UINT64_MASK
def _murmur64b(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
index = 0
seed_low = seed & UINT32_MASK
seed_high = (seed >> 32) & UINT32_MASK
h1 = (seed_low ^ length) & UINT32_MASK
h2 = seed_high & UINT32_MASK
while length >= 8:
k1 = _read_u32_le(data, index)
index += 4
length -= 4
k1 = _mul32(k1, m)
k1 ^= (k1 >> r) & UINT32_MASK
k1 = _mul32(k1, m)
h1 = _mul32(h1, m)
h1 ^= k1
k2 = _read_u32_le(data, index)
index += 4
length -= 4
k2 = _mul32(k2, m)
k2 ^= (k2 >> r) & UINT32_MASK
k2 = _mul32(k2, m)
h2 = _mul32(h2, m)
h2 ^= k2
if length >= 4:
k1 = _read_u32_le(data, index)
index += 4
length -= 4
k1 = _mul32(k1, m)
k1 ^= (k1 >> r) & UINT32_MASK
k1 = _mul32(k1, m)
h1 = _mul32(h1, m)
h1 ^= k1
if length > 0:
if length == 3:
h2 ^= data[index + 2] << 16
if length >= 2:
h2 ^= data[index + 1] << 8
if length >= 1:
h2 ^= data[index]
h2 = _mul32(h2, m)
h1 ^= (h2 >> 18) & UINT32_MASK
h1 = _mul32(h1, m)
h2 ^= (h1 >> 22) & UINT32_MASK
h2 = _mul32(h2, m)
h1 ^= (h2 >> 17) & UINT32_MASK
h1 = _mul32(h1, m)
h2 ^= (h1 >> 19) & UINT32_MASK
h2 = _mul32(h2, m)
return ((h1 << 32) | h2) & UINT64_MASK
def murmur2_hash(text: str, variant: Variant = "x86_32", seed: int = 0, output_format: OutputFormat = "number") -> str:
data = text.encode("utf-8")
if variant == "x86_32":
value = _murmur2_x86_32(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant == "x86_32a":
value = _murmur2a(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant in {"neutral_32", "aligned_32"}:
value = _murmur2_x86_32(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant == "x64_64a":
value = _murmur64a(data, seed)
return str(value) if output_format == "number" else f"{value:016x}"
value = _murmur64b(data, seed)
return str(value) if output_format == "number" else f"{value:016x}"
def murmur2_file(path: Path, variant: Variant = "x86_32", seed: int = 0, output_format: OutputFormat = "number") -> str:
return murmur2_hash(path.read_bytes().decode("utf-8", errors="replace"), variant, seed, output_format)
# Example usage
seed = parse_seed("0x2a")
print(murmur2_hash("hello", variant="x86_32", seed=seed, output_format="number"))
print(murmur2_hash("hello", variant="x64_64a", seed=seed, output_format="hex"))
with tempfile.TemporaryDirectory() as temp_dir:
sample_path = Path(temp_dir) / "sample.txt"
sample_path.write_text("hello", encoding="utf-8")
print(murmur2_file(sample_path, variant="x86_32", seed=seed, output_format="hex"))
Complete script (implementation + tests)
python
from pathlib import Path
from typing import Literal
Variant = Literal[
"x86_32",
"x86_32a",
"neutral_32",
"aligned_32",
"x64_64a",
"x86_64b",
]
OutputFormat = Literal["number", "hex"]
UINT32_MASK = 0xFFFFFFFF
UINT64_MASK = (1 << 64) - 1
def parse_seed(value: str) -> int:
if not value.strip():
return 0
cleaned = value.strip()
base = 16 if cleaned.startswith(("0x", "0X")) else 10
return int(cleaned, base) & UINT32_MASK
def _mul32(a: int, b: int) -> int:
return (a * b) & UINT32_MASK
def _read_u32_le(data: bytes, offset: int) -> int:
return int.from_bytes(data[offset : offset + 4], "little")
def _read_u64_le(data: bytes, offset: int) -> int:
return int.from_bytes(data[offset : offset + 8], "little") & UINT64_MASK
def _murmur2_x86_32(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
h = (seed ^ length) & UINT32_MASK
index = 0
while length >= 4:
k = _read_u32_le(data, index)
k = _mul32(k, m)
k ^= (k >> r) & UINT32_MASK
k = _mul32(k, m)
h = _mul32(h, m)
h ^= k
index += 4
length -= 4
if length == 3:
h ^= data[index + 2] << 16
if length >= 2:
h ^= data[index + 1] << 8
if length >= 1:
h ^= data[index]
h = _mul32(h, m)
h ^= (h >> 13) & UINT32_MASK
h = _mul32(h, m)
h ^= (h >> 15) & UINT32_MASK
return h & UINT32_MASK
def _mmix(h: int, k: int) -> int:
m = 0x5BD1E995
r = 24
k = _mul32(k, m)
k ^= (k >> r) & UINT32_MASK
k = _mul32(k, m)
h = _mul32(h, m)
h ^= k
return h & UINT32_MASK
def _murmur2a(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
h = seed & UINT32_MASK
index = 0
remaining = length
while remaining >= 4:
k = _read_u32_le(data, index)
h = _mmix(h, k)
index += 4
remaining -= 4
t = 0
if remaining == 3:
t ^= data[index + 2] << 16
if remaining >= 2:
t ^= data[index + 1] << 8
if remaining >= 1:
t ^= data[index]
h = _mmix(h, t)
h = _mmix(h, length)
h ^= (h >> 13) & UINT32_MASK
h = _mul32(h, m)
h ^= (h >> 15) & UINT32_MASK
return h & UINT32_MASK
def _murmur64a(data: bytes, seed: int) -> int:
m = 0xC6A4A7935BD1E995
r = 47
h = (seed ^ (len(data) * m)) & UINT64_MASK
index = 0
while index + 8 <= len(data):
k = _read_u64_le(data, index)
k = (k * m) & UINT64_MASK
k ^= (k >> r)
k = (k * m) & UINT64_MASK
h ^= k
h = (h * m) & UINT64_MASK
index += 8
remaining = len(data) - index
if remaining >= 7:
h ^= data[index + 6] << 48
if remaining >= 6:
h ^= data[index + 5] << 40
if remaining >= 5:
h ^= data[index + 4] << 32
if remaining >= 4:
h ^= data[index + 3] << 24
if remaining >= 3:
h ^= data[index + 2] << 16
if remaining >= 2:
h ^= data[index + 1] << 8
if remaining >= 1:
h ^= data[index]
h = (h * m) & UINT64_MASK
h ^= h >> r
h = (h * m) & UINT64_MASK
h ^= h >> r
return h & UINT64_MASK
def _murmur64b(data: bytes, seed: int) -> int:
m = 0x5BD1E995
r = 24
length = len(data)
index = 0
seed_low = seed & UINT32_MASK
seed_high = (seed >> 32) & UINT32_MASK
h1 = (seed_low ^ length) & UINT32_MASK
h2 = seed_high & UINT32_MASK
while length >= 8:
k1 = _read_u32_le(data, index)
index += 4
length -= 4
k1 = _mul32(k1, m)
k1 ^= (k1 >> r) & UINT32_MASK
k1 = _mul32(k1, m)
h1 = _mul32(h1, m)
h1 ^= k1
k2 = _read_u32_le(data, index)
index += 4
length -= 4
k2 = _mul32(k2, m)
k2 ^= (k2 >> r) & UINT32_MASK
k2 = _mul32(k2, m)
h2 = _mul32(h2, m)
h2 ^= k2
if length >= 4:
k1 = _read_u32_le(data, index)
index += 4
length -= 4
k1 = _mul32(k1, m)
k1 ^= (k1 >> r) & UINT32_MASK
k1 = _mul32(k1, m)
h1 = _mul32(h1, m)
h1 ^= k1
if length > 0:
if length == 3:
h2 ^= data[index + 2] << 16
if length >= 2:
h2 ^= data[index + 1] << 8
if length >= 1:
h2 ^= data[index]
h2 = _mul32(h2, m)
h1 ^= (h2 >> 18) & UINT32_MASK
h1 = _mul32(h1, m)
h2 ^= (h1 >> 22) & UINT32_MASK
h2 = _mul32(h2, m)
h1 ^= (h2 >> 17) & UINT32_MASK
h1 = _mul32(h1, m)
h2 ^= (h1 >> 19) & UINT32_MASK
h2 = _mul32(h2, m)
return ((h1 << 32) | h2) & UINT64_MASK
def murmur2_hash(text: str, variant: Variant = "x86_32", seed: int = 0, output_format: OutputFormat = "number") -> str:
data = text.encode("utf-8")
if variant == "x86_32":
value = _murmur2_x86_32(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant == "x86_32a":
value = _murmur2a(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant in {"neutral_32", "aligned_32"}:
value = _murmur2_x86_32(data, seed)
return str(value) if output_format == "number" else f"{value:08x}"
if variant == "x64_64a":
value = _murmur64a(data, seed)
return str(value) if output_format == "number" else f"{value:016x}"
value = _murmur64b(data, seed)
return str(value) if output_format == "number" else f"{value:016x}"
def murmur2_file(path: Path, variant: Variant = "x86_32", seed: int = 0, output_format: OutputFormat = "number") -> str:
return murmur2_hash(path.read_bytes().decode("utf-8", errors="replace"), variant, seed, output_format)
def run_tests() -> None:
assert murmur2_hash("", variant="x86_32", seed=0, output_format="hex") == "00000000"
assert murmur2_hash("hello", variant="x86_32", seed=0, output_format="hex") == "e56129cb"
assert murmur2_hash("hello", variant="x64_64a", seed=0, output_format="hex") == "1e68d17c457bf117"
print("MurmurHash2 tests passed")
if __name__ == "__main__":
run_tests()