HashSha256
HashSha256 computes the SHA-256 digest of some bytes: the same 32 bytes sha256sum, openssl dgst -sha256, PowerShell’s Get-FileHash, Go, Rust and Python compute for the same input. Use it when the digest is not yours to choose — a download to verify, a manifest to check, a content-addressed store to key.
// Verify a download against the digest its publisher printed.const Sha256Digest digest = HashSha256(downloaded_bytes);const String8 text = Sha256DigestToHex(scratch, digest);
if (!Str8Equal(text, published_hex)){ return Error::CorruptDownload;}HashSha256 is FIPS 180-4’s SHA-256, proved on NIST’s own published test vectors, so any conformant tool reproduces its values and it reproduces theirs. Basalt is not a cryptography library: there is no HMAC, no key derivation, no signatures, no constant-time guarantee and no security audit. It is for interop and for catching accidental corruption.
What the promise covers, and what it does not
Section titled “What the promise covers, and what it does not”The digest
Section titled “The digest”A digest is 32 bytes returned by value — no allocation, no out-parameter:
struct Sha256Digest{ static constexpr usize digest_size = 32; u8 bytes[digest_size]; // bytes[0] is the first hex pair sha256sum prints};The bytes are in the standard’s order, so you can write them straight into a file, compare them to another tool’s output byte for byte, or format them:
const String8 text = Sha256DigestToHex(arena, digest); // 64 lowercase hex characters// or, for any bytes at all:const String8 dump = Str8Hex(arena, Str8(header, sizeof(header)));Sha256DigestToHex is one line over Str8Hex, which lives in String.h because hex formatting is a string concern and serves file headers and debug dumps just as well. Both results are NUL-terminated past their end, so they can be passed to a %s too.
Digests compare with == and !=. There is deliberately no integer view and no ordering: which end would be “the top” is exactly the byte-order question a byte array avoids.
Hashing something too large to hold
Section titled “Hashing something too large to hold”The streaming form is Init / Update* / Final, and the state is plain data — 104 bytes, no allocation, no destructor:
HashSha256State state = HashSha256Initialize();
u8 chunk[64 * 1024];usize read = 0;while (ReadChunk(file, chunk, sizeof(chunk), &read)){ HashSha256Update(state, Str8(chunk, read));}
const Sha256Digest digest = HashSha256Final(state);Four rules make it predictable:
| Chunking never matters | Update(a); Update(b) is exactly Update(a ++ b), for any split. Zero-length updates are legal and change nothing. |
Final does not consume |
It takes the state by const& and pads a copy, so you can take the digest of a prefix and keep going. |
| Copy is snapshot | The state is a POD; copying it forks the stream. There is no Reset — re-Init instead. |
| Same values as the one-shot | HashSha256(bytes) is the streaming path fed once, through the same compression function. |
One thing the streaming form does not do: it is not prefix-free. Update("ab") + Update("c") and Update("a") + Update("bc") are the same message. If you are digesting several fields, put your own separators or lengths between them.
When this is the wrong tool
Section titled “When this is the wrong tool”- Keying an in-memory table.
HashFast— forty times faster and free to get faster still. - A stable 64-bit content identity for a cache header or a file format of your own.
HashRapidV3Nanois frozen too, and far cheaper. - Authenticating data against a hostile writer. Use a vetted cryptography library; see the danger note above.
API summary
Section titled “API summary”Parameters, assertions and the full contract are documented in basalt/core/hash/HashSha256.h.
| Signature | Purpose |
|---|---|
HashSha256(String8 bytes) |
The 32-byte FIPS 180-4 digest of the bytes. |
HashSha256Initialize() |
A fresh streaming state. |
HashSha256Update(HashSha256State&, String8 bytes) |
Append bytes to the message. |
HashSha256Final(const HashSha256State&) |
The digest so far. Does not consume the state. |
Sha256DigestToHex(Arena&, const Sha256Digest&) |
The 64 lowercase hex characters sha256sum prints. |
Str8Hex(Arena&, String8 bytes) |
Lowercase hex for any bytes (in String.h). |