diff --git a/lix/libutil/hash.cc b/lix/libutil/hash.cc index 137a4bc87..4350c4bc0 100644 --- a/lix/libutil/hash.cc +++ b/lix/libutil/hash.cc @@ -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 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 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 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 {}; + 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. diff --git a/lix/libutil/hash.hh b/lix/libutil/hash.hh index c71044bee..e760b35f6 100644 --- a/lix/libutil/hash.hh +++ b/lix/libutil/hash.hh @@ -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 hashTypes; +extern const std::set 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 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 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.