Skip to content

Commit

Permalink
Merge pull request #14 from RossComputerGuy/feat/zig-hash
Browse files Browse the repository at this point in the history
feat(libutil): implement hashing in zig
  • Loading branch information
RossComputerGuy authored Feb 7, 2025
2 parents 4d8b5d4 + bc73844 commit 0d87248
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 70 deletions.
64 changes: 14 additions & 50 deletions src/libutil/hash.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
#include <iostream>
#include <cstring>

#include <blake3.h>
#include <openssl/crypto.h>
#include <openssl/md5.h>
#include <openssl/sha.h>

#include "args.hh"
#include "hash.hh"
#include "archive.hh"
Expand Down Expand Up @@ -287,55 +282,26 @@ Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashAlgorithm> ha
return Hash::parseAny(hashStr, ha);
}

struct Ctx {};

union Ctx
{
blake3_hasher blake3;
MD5_CTX md5;
SHA_CTX sha1;
SHA256_CTX sha256;
SHA512_CTX sha512;
};


static void start(HashAlgorithm ha, Ctx & ctx)
{
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_init(&ctx.blake3);
else if (ha == HashAlgorithm::MD5) MD5_Init(&ctx.md5);
else if (ha == HashAlgorithm::SHA1) SHA1_Init(&ctx.sha1);
else if (ha == HashAlgorithm::SHA256) SHA256_Init(&ctx.sha256);
else if (ha == HashAlgorithm::SHA512) SHA512_Init(&ctx.sha512);
}

extern "C" Ctx* nix_libutil_hash_create(HashAlgorithm type);
extern "C" void nix_libutil_hash_update(Ctx* ctx, const void* data, size_t size);
extern "C" void nix_libutil_hash_finish(Ctx* ctx, uint8_t* hash);

static void update(HashAlgorithm ha, Ctx & ctx,
static void update(Ctx* ctx,
std::string_view data)
{
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_update(&ctx.blake3, data.data(), data.size());
else if (ha == HashAlgorithm::MD5) MD5_Update(&ctx.md5, data.data(), data.size());
else if (ha == HashAlgorithm::SHA1) SHA1_Update(&ctx.sha1, data.data(), data.size());
else if (ha == HashAlgorithm::SHA256) SHA256_Update(&ctx.sha256, data.data(), data.size());
else if (ha == HashAlgorithm::SHA512) SHA512_Update(&ctx.sha512, data.data(), data.size());
}


static void finish(HashAlgorithm ha, Ctx & ctx, unsigned char * hash)
{
if (ha == HashAlgorithm::BLAKE3) blake3_hasher_finalize(&ctx.blake3, hash, BLAKE3_OUT_LEN);
else if (ha == HashAlgorithm::MD5) MD5_Final(hash, &ctx.md5);
else if (ha == HashAlgorithm::SHA1) SHA1_Final(hash, &ctx.sha1);
else if (ha == HashAlgorithm::SHA256) SHA256_Final(hash, &ctx.sha256);
else if (ha == HashAlgorithm::SHA512) SHA512_Final(hash, &ctx.sha512);
nix_libutil_hash_update(ctx, data.data(), data.size());
}

Hash hashString(
HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings)
{
Ctx ctx;
Ctx* ctx = nix_libutil_hash_create(ha);
Hash hash(ha, xpSettings);
start(ha, ctx);
update(ha, ctx, s);
finish(ha, ctx, hash.hash);
update(ctx, s);
nix_libutil_hash_finish(ctx, &hash.hash[0]);
delete ctx;
return hash;
}

Expand All @@ -349,9 +315,8 @@ Hash hashFile(HashAlgorithm ha, const Path & path)

HashSink::HashSink(HashAlgorithm ha) : ha(ha)
{
ctx = new Ctx;
ctx = nix_libutil_hash_create(ha);
bytes = 0;
start(ha, *ctx);
}

HashSink::~HashSink()
Expand All @@ -363,23 +328,22 @@ HashSink::~HashSink()
void HashSink::writeUnbuffered(std::string_view data)
{
bytes += data.size();
update(ha, *ctx, data);
update(ctx, data);
}

HashResult HashSink::finish()
{
flush();
Hash hash(ha);
nix::finish(ha, *ctx, hash.hash);
nix_libutil_hash_finish(ctx, &hash.hash[0]);
return HashResult(hash, bytes);
}

HashResult HashSink::currentHash()
{
flush();
Ctx ctx2 = *ctx;
Hash hash(ha);
nix::finish(ha, ctx2, hash.hash);
nix_libutil_hash_finish(ctx, &hash.hash[0]);
return HashResult(hash, bytes);
}

Expand Down
2 changes: 1 addition & 1 deletion src/libutil/hash.hh
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ std::optional<HashAlgorithm> parseHashAlgoOpt(std::string_view s);
std::string_view printHashAlgo(HashAlgorithm ha);


union Ctx;
struct Ctx;

struct AbstractHashSink : virtual Sink
{
Expand Down
79 changes: 79 additions & 0 deletions src/libutil/hash.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const std = @import("std");

pub const HashAlgorithm = enum(c_char) {
md5 = 42,
sha1,
sha256,
sha512,
blake3,
};

pub const Ctx = union(HashAlgorithm) {
md5: std.crypto.hash.Md5,
sha1: std.crypto.hash.Sha1,
sha256: std.crypto.hash.sha2.Sha256,
sha512: std.crypto.hash.sha2.Sha512,
blake3: std.crypto.hash.Blake3,

pub const Extern = opaque {};

pub inline fn toExtern(self: *Ctx) *Extern {
return @ptrCast(@alignCast(self));
}

pub inline fn fromExtern(e: *Extern) *Ctx {
return @ptrCast(@alignCast(e));
}
};

pub fn create(t: HashAlgorithm) callconv(.C) *Ctx.Extern {
const self = std.heap.c_allocator.create(Ctx) catch @panic("OOM");
inline for (comptime std.meta.fields(Ctx)) |field| {
const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable;
if (v == t) {
self.* = @unionInit(Ctx, field.name, field.type.init(.{}));
return self.toExtern();
}
}
unreachable;
}

pub fn update(e: *Ctx.Extern, raw_data: [*]const u8, size: usize) callconv(.C) void {
const self = Ctx.fromExtern(e);

const data = raw_data[0..size];

inline for (comptime std.meta.fields(Ctx)) |field| {
const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable;
if (self.* == v) {
@field(self, field.name).update(data);
return;
}
}

unreachable;
}

pub fn finish(e: *Ctx.Extern, raw_hash: [*:0]u8) callconv(.C) void {
const self = Ctx.fromExtern(e);

inline for (comptime std.meta.fields(Ctx)) |field| {
const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable;
const size = field.type.digest_length;
if (self.* == v) {
std.debug.assert(size <= 64);
const hash = raw_hash[0..size];
@field(self, field.name).final(hash[0..size]);
@memset(raw_hash[size..64], 0);
return;
}
}

unreachable;
}

comptime {
@export(&create, .{ .name = "nix_libutil_hash_create" });
@export(&update, .{ .name = "nix_libutil_hash_update" });
@export(&finish, .{ .name = "nix_libutil_hash_finish" });
}
2 changes: 2 additions & 0 deletions src/libutil/libutil.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub const cpuid = @import("cpuid.zig");
pub const hash = @import("hash.zig");

comptime {
_ = cpuid;
_ = hash;
}
18 changes: 3 additions & 15 deletions src/libutil/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,6 @@ elif host_machine.system() == 'sunos'
deps_other += [socket, network_service_library]
endif

blake3 = dependency(
'libblake3',
version: '>= 1.5.5',
)
deps_private += blake3

boost = dependency(
'boost',
modules : ['context', 'coroutine'],
Expand All @@ -79,13 +73,6 @@ boost = dependency(
# put in `deps_other`.
deps_other += boost

openssl = dependency(
'libcrypto',
'openssl',
version : '>= 1.1.1',
)
deps_private += openssl

libarchive = dependency('libarchive', version : '>= 3.1.2')
deps_public += libarchive
if get_option('default_library') == 'static'
Expand Down Expand Up @@ -269,11 +256,12 @@ subdir('nix-meson-build-support/windows-version')

sources += custom_target(
'zig build-obj',
command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libutil-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', zig_args, '@INPUT@'],
command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libutil-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', '-lc', zig_args, '@INPUT@'],
output: 'libutil-zig.c',
input: 'libutil.zig',
depend_files: [
'cpuid.zig'
'cpuid.zig',
'hash.zig',
],
)

Expand Down
4 changes: 0 additions & 4 deletions src/libutil/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
boost,
brotli,
libarchive,
libblake3,
libsodium,
nlohmann_json,
openssl,

# Configuration Options

Expand Down Expand Up @@ -46,9 +44,7 @@ mkMesonLibrary (finalAttrs: {

buildInputs = [
brotli
libblake3
libsodium
openssl
];

propagatedBuildInputs = [
Expand Down

0 comments on commit 0d87248

Please sign in to comment.