When I needed to switch types I was using a simple enum with a function to return a type I need from it
/// Enum for Zig supported hashing algorithms
pub const ChecksumAlgo = enum {
blake3,
sha256,
sha512,
sha1, // Legacy
md5, // Legacy
sha3_256,
sha3_512,
blake2b,
blake2s,
ascon_hash,
xxh3,
/// Return a type based upon an enum
fn HashType(comptime self: ChecksumAlgo) type {
return switch (self) {
.blake3 => std.crypto.hash.Blake3,
.sha256 => std.crypto.hash.sha2.Sha256,
.sha512 => std.crypto.hash.sha2.Sha512,
.md5 => std.crypto.hash.Md5,
.sha1 => std.crypto.hash.Sha1,
.sha3_256 => std.crypto.hash.sha3.Sha3_256,
.sha3_512 => std.crypto.hash.sha3.Sha3_512,
.blake2b => std.crypto.hash.blake2.Blake2b256,
.blake2s => std.crypto.hash.blake2.Blake2s256,
.ascon_hash => std.crypto.hash.ascon.AsconHash256,
.xxh3 => std.hash.XxHash3,
};
}
};
Then I was passing it where I needed it and based upon returned type had a different logic for hashing:
/// Worker for hashing function
fn hashWorker(
comptime T: type,
reader: *Io.Reader,
result: *[getDigestLen(T)]u8, // Pointer to store the final hash
name: []const u8,
) !void {
std.log.debug("Started hashWorker for {s}", .{name});
// Run hashStream and write directly into the result pointer
const hash = try hashStream(T, reader);
@memcpy(result, &hash);
std.log.debug("Finished hashWorker for {s}", .{name});
}
/// Uses a userspace to calculate and return a hash for streams
fn hashStream(comptime T: type, instream: *Io.Reader) ![getDigestLen(T)]u8 {
const digest_len = comptime getDigestLen(T);
// Crypto hashes usually take Options; Non-crypto usually take a u64 seed.
var h = if (@hasDecl(T, "Options"))
T.init(.{})
else if (@hasDecl(T, "init"))
T.init(0)
else
T.init();
var result: [digest_len]u8 = undefined;
while (true) {
const data = instream.take(instream.buffer.len) catch |err| switch (err) {
error.EndOfStream => {
const buffered = instream.buffered();
if (buffered.len != 0) h.update(buffered);
break;
},
else => return err,
};
h.update(data);
}
// Check return type of a hasher
const FinalReturnType = @typeInfo(@TypeOf(T.final)).@"fn".return_type.?;
if (FinalReturnType == void) {
// Cryptographic hashing
h.final(&result);
} else {
// Non-crypto hashing
const digest = h.final();
// Convert integer to bytes (Big Endian is standard for hash display)
std.mem.writeInt(FinalReturnType, &result, digest, .big);
}
// Always return array of bytes
return result;
}