Skip to content

Choosing a hash

Basalt ships one hash for tables and a collection of hashes for everything else. The first is HashFast: the value is valid inside one process of one build, and the algorithm behind it may change in any version. Every other hash on these pages is a member of the stable collection: it is a published algorithm, named by its full identity, whose output for a given input never changes — across Basalt versions, platforms, compilers and years.

Ask first whether the value outlives the process. If it does not, use HashFast and stop reading. If it does, the job usually names the algorithm for you — a file format, a tool you must agree with, a guarantee you need — and the table below is how to find that name.

HashFast A collection member
The value is valid within one process of one build forever, everywhere
Persist or transmit it never the intended use
The algorithm may change in any version never — a better one joins under a new name
Extensible to your own key types yes, by overload no — bytes in, value out
Use it for table keys, in-memory caches file formats, checksums, digests other tools verify

Today HashFast forwards to HashRapidV3Nano, so the two return the same value for the same bytes. Only the value obtained under the stable name is one you may keep: HashFast is free to forward elsewhere tomorrow.

Every member is a pure function of the bytes: the buffer’s address, its alignment and the machine’s byte order change nothing. Throughput is on a 1 MB input, msvc-release on an Intel i7-12650H laptop, to one significant digit — each member’s own page has the full table by input size.

Member Output Reach for it when 1 MB throughput
HashFnv1a32 / HashFnv1a64 u32 / u64 You need a constant at compile time from a string literal, or must reproduce an FNV value another system emitted ~1 GB/s
HashCrc32 u32 A format specifies it: gzip, zip, PNG, Ethernet ~2 GB/s
HashCrc32C u32 You want to detect accidental corruption with a guarantee (every burst of 32 bits or fewer, every 5-bit error to 655 bytes), or a storage format names it ~2 GB/s, ~30 GB/s with SSE4.2
HashXxh64 u64 You must agree with the most widely adopted stable 64-bit hash (Zstandard, .NET, Go), or you need to stream an input you never hold whole ~16 GB/s
HashRapidV3Nano u64 A stable 64-bit identity for short content — names, keys, small records ~30 GB/s
HashRapidV3 u64 A stable 64-bit identity for content at volume — files, assets, buffers over a kilobyte ~30 GB/s, and the fastest above 1 KiB
HashSha256 32 bytes A digest another tool will verify (sha256sum, a manifest, a content-addressed store), or one that must hold against someone choosing the input ~0.4 GB/s

A few of those rows deserve a sentence.

HashRapidV3 and HashRapidV3Nano are the same function up to 48 bytes, value for value, and different functions above. The measured crossover where the full one becomes faster is about a kilobyte, not 48 bytes: between the two, Nano’s shorter tail still wins. Pick by what you hash, and either name is a frozen contract.

A CRC is not a hash. It offers a proof bounded by message length where a hash offers a probability, and it is the only kind of member that does. The price is linearity: appending computed bytes forces any CRC value you like, so it detects accidents and nothing else. Prefer HashCrc32C for new work; HashCrc32 exists because gzip, zip and PNG made the choice before the instruction existed.

FNV-1a is the weakest member, on purpose. It ships because it is the one hash the compiler can run, and because Roslyn, Unreal, EnTT and Kubernetes emit its values. It must never key a Basalt map.

A member’s surface is drawn from four shapes, so having used one you have used them all.

One-shot — every member: HashXxh64(bytes), HashCrc32C(bytes), HashSha256(bytes). The empty input is a legal input with an ordinary value, never a sentinel.

Seeded — where the published identity has a seed: HashXxh64(bytes, seed), HashRapidV3(bytes, seed), HashRapidV3Nano(bytes, seed). HashX(bytes) equals HashX(bytes, 0).

Chained — where the running value is the state, as for the CRCs and FNV: HashCrc32CExtend(previous, bytes), starting from 0 for a CRC and from the offset basis for FNV.

u32 crc = 0;
while (ReadChunk(file, chunk))
{
crc = HashCrc32CExtend(crc, chunk);
}

Stateful — where the state is wider than the value, as for HashXxh64 and HashSha256: a POD struct you Init, Update and Final. Final takes the state by const& and does not consume it, so “the digest of what I have so far, then carry on” is one call; copying the state forks the stream.

HashSha256State state = HashSha256Initialize();
while (ReadChunk(file, chunk))
{
HashSha256Update(state, chunk);
}
const Sha256Digest digest = HashSha256Final(state);
const String8 hex = Sha256DigestToHex(arena, digest); // what sha256sum prints

Two laws hold for every chained and stateful member, and the test suites pin them at every split point: feeding a then b gives the hash of a ++ b, and the streamed value equals the one-shot. One warning follows from the first law: no member is prefix-free. Hashing several fields in sequence is hashing their concatenation, so inject your own separators or lengths.

Each is a decision with a trigger, recorded in the family specification. A keyed hash (SipHash) — declined, because a secret key is the one thing the collection says never passes through it. SHA-1 and MD5 — interop-only and collision-broken; they join the day a real consumer reads git object names or Content-MD5. XXH3, XXH128 and BLAKE3 — SIMD algorithms with a dispatch surface of their own, waiting for a measured shortfall of HashRapidV3. A runtime enum of algorithms — never: the identity lives in the name.

Page What it is
HashFast The in-memory hash the maps key on. Not a collection member.
HashRapidV3Nano The stable 64-bit hash for short content, and what HashFast forwards to today.
HashRapidV3 The stable 64-bit hash for content at volume.
HashXxh64 The most widely adopted stable 64-bit hash, with a streaming form.
HashCrc32 / HashCrc32C Error detection with a proof, and interop with the formats that specify a CRC.
HashFnv1a32 / HashFnv1a64 The compile-time member, and interop with FNV-emitting systems.
HashSha256 The interop digest.