libutil/hash: Simplify some parts

Use a span representing the underlying hash, this allows comparing based
on those spans. Other minor tweaks to the header file.

Change-Id: Ie05960439e60500109083c06d347d6cdf41bfda8
This commit is contained in:
Tom Hubrecht
2026-01-12 15:56:05 +01:00
parent 45783a0435
commit f431464382
2 changed files with 85 additions and 93 deletions
+37 -78
View File
@@ -16,53 +16,7 @@
namespace nix {
static size_t regularHashSize(HashType type) {
switch (type) {
case HashType::MD5: return md5HashSize;
case HashType::SHA1: return sha1HashSize;
case HashType::SHA256: return sha256HashSize;
case HashType::SHA512: return sha512HashSize;
}
abort();
}
std::set<std::string> hashTypes = { "md5", "sha1", "sha256", "sha512" };
Hash::Hash(HashType type) : type(type)
{
hashSize = regularHashSize(type);
assert(hashSize <= maxHashSize);
memset(hash, 0, maxHashSize);
}
bool Hash::operator == (const Hash & h2) const
{
if (hashSize != h2.hashSize) return false;
for (unsigned int i = 0; i < hashSize; i++)
if (hash[i] != h2.hash[i]) return false;
return true;
}
bool Hash::operator != (const Hash & h2) const
{
return !(*this == h2);
}
bool Hash::operator < (const Hash & h) const
{
if (hashSize < h.hashSize) return true;
if (hashSize > h.hashSize) return false;
for (unsigned int i = 0; i < hashSize; i++) {
if (hash[i] < h.hash[i]) return true;
if (hash[i] > h.hash[i]) return false;
}
return false;
}
const std::set<std::string> hashTypes = {"md5", "sha1", "sha256", "sha512"};
const std::string base16Chars = "0123456789abcdef";
@@ -247,7 +201,6 @@ static void update(detail::EvpMdCtxPtr & ctx, std::string_view data)
}
}
static void finish(detail::EvpMdCtxPtr & ctx, unsigned char * hash)
{
if (!EVP_DigestFinal_ex(ctx.get(), hash, NULL)) {
@@ -255,7 +208,6 @@ static void finish(detail::EvpMdCtxPtr & ctx, unsigned char * hash)
}
}
Hash hashString(HashType ht, std::string_view s)
{
Hash hash(ht);
@@ -265,7 +217,6 @@ Hash hashString(HashType ht, std::string_view s)
return hash;
}
Hash hashFile(HashType ht, const Path & path)
{
HashSink sink(ht);
@@ -273,11 +224,7 @@ Hash hashFile(HashType ht, const Path & path)
return sink.finish().first;
}
HashSink::HashSink(HashType ht) : ht(ht), ctx(start(ht))
{
bytes = 0;
}
HashSink::HashSink(HashType ht) : ht(ht), ctx(start(ht)), bytes(0) {}
void HashSink::writeUnbuffered(std::string_view data)
{
@@ -305,7 +252,6 @@ HashResult HashSink::currentHash()
return HashResult(hash, bytes);
}
HashResult hashPath(HashType ht, const PreparedDump & path)
{
HashSink sink(ht);
@@ -313,42 +259,55 @@ HashResult hashPath(HashType ht, const PreparedDump & path)
return sink.finish();
}
Hash compressHash(const Hash & hash, unsigned int newSize)
Hash compressHash(const Hash & hash, size_t newSize)
{
Hash h(hash.type);
h.hashSize = newSize;
for (unsigned int i = 0; i < hash.hashSize; ++i)
h.hash[i % newSize] ^= hash.hash[i];
Hash h(newSize, hash.type);
for (const auto [idx, c] : enumerate(hash.as_span())) {
h.hash[idx % newSize] ^= c;
}
return h;
}
std::optional<HashType> parseHashTypeOpt(std::string_view s)
{
if (s == "md5") return HashType::MD5;
else if (s == "sha1") return HashType::SHA1;
else if (s == "sha256") return HashType::SHA256;
else if (s == "sha512") return HashType::SHA512;
else return std::optional<HashType> {};
if (s == "md5") {
return HashType::MD5;
}
if (s == "sha1") {
return HashType::SHA1;
}
if (s == "sha256") {
return HashType::SHA256;
}
if (s == "sha512") {
return HashType::SHA512;
}
return std::nullopt;
}
HashType parseHashType(std::string_view s)
{
auto opt_h = parseHashTypeOpt(s);
if (opt_h)
if (auto opt_h = parseHashTypeOpt(s)) {
return *opt_h;
else
throw UsageError("unknown hash algorithm '%1%'", s);
}
throw UsageError("unknown hash algorithm '%1%'", s);
}
std::string_view printHashType(HashType ht)
std::string_view printHashType(HashType type)
{
switch (ht) {
case HashType::MD5: return "md5";
case HashType::SHA1: return "sha1";
case HashType::SHA256: return "sha256";
case HashType::SHA512: return "sha512";
switch (type) {
case HashType::MD5:
return "md5";
case HashType::SHA1:
return "sha1";
case HashType::SHA256:
return "sha256";
case HashType::SHA512:
return "sha512";
default:
// illegal hash type enum value internally, as opposed to external input
// which should be validated with nice error message.
+48 -15
View File
@@ -20,13 +20,27 @@ MakeError(BadHash, Error);
enum class HashType : char { MD5 = 42, SHA1, SHA256, SHA512 };
const size_t md5HashSize = 16;
const size_t sha1HashSize = 20;
const size_t sha256HashSize = 32;
const size_t sha512HashSize = 64;
const int md5HashSize = 16;
const int sha1HashSize = 20;
const int sha256HashSize = 32;
const int sha512HashSize = 64;
static constexpr size_t regularHashSize(HashType type)
{
switch (type) {
case HashType::MD5:
return md5HashSize;
case HashType::SHA1:
return sha1HashSize;
case HashType::SHA256:
return sha256HashSize;
case HashType::SHA512:
return sha512HashSize;
}
abort();
}
extern std::set<std::string> hashTypes;
extern const std::set<std::string> hashTypes;
/**
* @brief Enumeration representing the hash formats.
@@ -55,7 +69,13 @@ struct Hash
/**
* Create a zero-filled hash object.
*/
Hash(HashType type);
Hash(size_t hashSize, HashType type) : hashSize(hashSize), type(type)
{
assert(hashSize <= maxHashSize);
memset(hash, 0, maxHashSize);
}
Hash(HashType type) : Hash(regularHashSize(type), type) {}
/**
* Parse the hash from a string representation in the format
@@ -91,17 +111,22 @@ public:
/**
* Check whether two hash are equal.
*/
bool operator == (const Hash & h2) const;
/**
* Check whether two hash are not equal.
*/
bool operator != (const Hash & h2) const;
bool operator==(const Hash & other) const
{
return std::ranges::equal(as_span(), other.as_span());
}
/**
* For sorting.
*/
bool operator < (const Hash & h) const;
std::strong_ordering operator<=>(const Hash & other) const
{
std::span<const uint8_t> lhs = as_span(), rhs = other.as_span();
return (lhs.size() == rhs.size())
? std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end())
: lhs.size() <=> rhs.size();
}
/**
* Returns the length of a base-16 representation of this hash.
@@ -127,6 +152,14 @@ public:
return ((4 * hashSize / 3) + 3) & ~3;
}
/**
* Returns a span of the intrinsic hash
*/
std::span<const uint8_t> as_span() const
{
return {hash, hashSize};
}
/**
* Return a string representation of the hash, in base-16, base-32
* or base-64. By default, this is prefixed by the hash type
@@ -141,7 +174,7 @@ public:
std::string gitShortRev() const
{
return std::string(to_string(HashFormat::Base16, false), 0, 7);
return std::string(gitRev(), 0, 7);
}
static Hash dummy;
@@ -177,7 +210,7 @@ inline HashResult hashPath(HashType ht, Path path)
* Compress a hash to the specified number of bytes by cyclically
* XORing bytes together.
*/
Hash compressHash(const Hash & hash, unsigned int newSize);
Hash compressHash(const Hash & hash, size_t newSize);
/**
* Parse a string representing a hash type.