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.
Two contracts
Section titled “Two contracts”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.
The collection, from fastest to strongest
Section titled “The collection, from fastest to strongest”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.
What no member promises
Section titled “What no member promises”The shapes every member shares
Section titled “The shapes every member shares”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 printsTwo 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.
What is deliberately not here
Section titled “What is deliberately not here”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.
The pages
Section titled “The pages”| 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. |