Skip to content

Repository files navigation

hayahash

CI license

hayahash is a family of fast, non-cryptographic 64- and 128-bit hash functions for platforms with ordinary 64-bit scalar arithmetic. It passes the full SMHasher3 suite without requiring SIMD, AES instructions, or a 64x64-to-128-bit multiply.

The reference implementation is the single C header hayahash.h. Bit-exact ports are available for Rust, Go, Zig, Java, C#, Python, Swift, JavaScript/TypeScript, and MIPS64 assembly. Every 128-bit API returns ordered lo and hi words, with hayahash128.lo == hayahash64 for the same input and seed.

Experimental: the algorithm, constants, and digest values may still change. Do not use hayahash yet where hashes must remain stable across versions. It is not a cryptographic hash or message authentication code. See docs/stability.md for the 1.0 freeze criteria and SECURITY.md for the threat model.

Haya (速) is Japanese for "fast."

Why hayahash

  • Portable scalar core: ordinary 64-bit operations, endian-independent output, no undefined behavior, and no architecture-specific intrinsics.
  • Two output widths: hayahash64 and hayahash128 share the same state walk; selecting 128 bits adds a second finalization path rather than a second pass over the input.
  • Streaming: one-shot and incremental APIs produce identical digests for every split of the same input, in the C reference and in every port.
  • Broad language support: every maintained port exposes both widths and is checked against the C reference.
  • Quality-gated development: both widths pass all 188 applicable SMHasher3 test groups, with additional structured-collision and differential tests.

The design is aimed at wasm, JVM, .NET, portable C, and other environments where wide multiplication or hardware acceleration cannot be assumed. It is not a universal speed claim: hashes that target native wide multiply, SIMD, or AES instructions can be faster on hardware that provides them. See the benchmark record for competitive results and methodology.

Performance

Representative measurements of the public 64- and 128-bit APIs:

host / compiler 8 B chained, 64 / 128 (ns) 1 MiB, 64 / 128 (GB/s)
Apple M1 Pro / Apple clang 21 7.88 / 9.25 30.73 / 30.76
Ryzen AI 9 HX PRO 370 / GCC 16 4.29 / 4.90 61.47 / 61.31
EPYC 9655 KVM guest / GCC 13 4.92 / 5.66 54.15 / 53.99
wasm32 on M1 Pro / Zig 0.16 7.7 / 10.6 23.55 / 23.34

The M1 and Ryzen native runs are bare metal. The EPYC guest has no frequency control, so its within-host ratio is more meaningful than its absolute rate. The 128-bit dispatch change recorded in the optimization log brought 128-bit bulk to parity with the 64-bit rate on all three machines. The wasm build uses no SIMD or wide multiply. These are point measurements, not a claim that one hash is fastest on every workload or machine.

Detailed size sweeps, ChibiHash comparisons, SMHasher3 shootouts, caveats, and reproduction notes are in docs/benchmarks.md.

Usage

C - copy hayahash.h into your project, or install the header and pkg-config file with make install:

#include "hayahash.h"

uint64_t h = hayahash64(buf, len, seed);
hayahash128_t h128 = hayahash128(buf, len, seed);
// h128.lo == h
make install PREFIX=/usr/local
cc $(pkg-config --cflags hayahash) main.c -o main

CMake installs the same header and pkg-config file to the same paths, and adds a package config for find_package:

cmake -S . -B build && cmake --install build --prefix /usr/local
find_package(hayahash 0.5 REQUIRED)
target_link_libraries(app PRIVATE hayahash::hayahash)

Streaming uses one shared state for both output widths. Calling a digest function does not modify the state:

hayahash64_state st;
hayahash64_init(&st, seed);
hayahash64_update(&st, part1, n1);
hayahash64_update(&st, part2, n2);
uint64_t h = hayahash64_digest(&st);
hayahash128_t h128 = hayahash128_digest(&st);
language package 64-bit call 128-bit call
Rust hayahash (no_std) hayahash::hayahash64(buf, seed) hayahash::hayahash128(buf, seed)
Go github.com/thevilledev/hayahash/go hayahash.Hash64(buf, seed) hayahash.Hash128(buf, seed)
Zig hayahash module (Zig 0.16) hayahash.hayahash64(buf, seed) hayahash.hayahash128(buf, seed)
Java io.github.thevilledev:hayahash (17+) Hayahash.hash64(buf, seed) Hayahash.hash128(buf, seed)
C# Hayahash (.NET 8+) Hayahash.Hash64(buf, seed) Hayahash.Hash128(buf, seed)
Python hayahash (3.9+) hayahash64(buf, seed) hayahash128(buf, seed)
Swift Hayahash SwiftPM package (5.9+) Hayahash.hash64(buf, seed: 0) Hayahash.hash128(buf, seed: 0)
JS/TS hayahash (wasm + pure JS) hayahash64(buf, seed) hayahash128(buf, seed)
MIPS64 hayahash.S (n64 ABI) hayahash64(buf, len, seed) hayahash128(buf, len, seed)

The table lists the one-shot entry points. Every port also provides the incremental API shown above, spelled the way that language spells it - Digest in Go (a hash.Hash64) and Rust, Hayahash.Hasher in Swift, Hasher elsewhere. See docs/ports.md.

Installation details, complete examples, and the repository layout are in docs/ports.md.

Quality

hayahash64 and hayahash128 each pass all 188 applicable SMHasher3 test groups. CI also checks structured collision sets, one-shot/streaming equality, cross-language differential conformance, big-endian output, wasm32, MSVC x64, and the MIPS64 n64 ABI. The exact tests, verification values, and limitations are documented in docs/quality.md.

Tools

cli/hayasum hashes files or stdin with the C reference:

make -C cli
./cli/hayasum -b 128 README.md

Its own tests and fuzz targets live under cli/: make -C cli check runs the functional harness and replays the fuzz corpus, make -C cli fuzz-run fuzzes argv parsing, the reader, and the output escaper.

Documentation

License

Public domain under the Unlicense. The separately licensed SMHasher3 test harness is described in docs/smhasher3.md.

About

Experimental fast, portable 64- and 128-bit non-cryptographic hash functions

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages