From: Joel Rosdahl Date: Wed, 8 Jul 2020 07:51:10 +0000 (+0200) Subject: C++-ify struct hash X-Git-Tag: v4.0~336 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a502145296044ea32b24e329d75122e777c598e6;p=thirdparty%2Fccache.git C++-ify struct hash --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 29838355e..1d3d25ed6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ set( Context.cpp Counters.cpp Decompressor.cpp + Hash.cpp Lockfile.cpp MiniTrace.cpp NullCompressor.cpp @@ -32,7 +33,6 @@ set( compopt.cpp compress.cpp execute.cpp - hash.cpp hashutil.cpp language.cpp legacy_util.cpp diff --git a/src/Context.hpp b/src/Context.hpp index 8e6c332e7..2ab6052ce 100644 --- a/src/Context.hpp +++ b/src/Context.hpp @@ -23,11 +23,11 @@ #include "Args.hpp" #include "ArgsInfo.hpp" #include "Config.hpp" +#include "Digest.hpp" #include "File.hpp" #include "MiniTrace.hpp" #include "NonCopyable.hpp" #include "ccache.hpp" -#include "hash.hpp" #ifdef INODE_CACHE_SUPPORTED # include "InodeCache.hpp" diff --git a/src/Hash.cpp b/src/Hash.cpp new file mode 100644 index 000000000..2812c2d75 --- /dev/null +++ b/src/Hash.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Hash.hpp" + +#include "Fd.hpp" +#include "logging.hpp" + +using nonstd::string_view; + +const string_view HASH_DELIMITER("\000cCaChE\000", 8); + +Hash::Hash() +{ + blake3_hasher_init(&m_hasher); +} + +void +Hash::enable_debug(string_view section_name, + FILE* debug_binary, + FILE* debug_text) +{ + m_debug_binary = debug_binary; + m_debug_text = debug_text; + + add_debug_text("=== "); + add_debug_text(section_name); + add_debug_text(" ===\n"); +} + +Digest +Hash::digest() const +{ + // Note that blake3_hasher_finalize doesn't modify the hasher itself, thus it + // is possible to finalize again after more data has been added. + Digest digest; + blake3_hasher_finalize(&m_hasher, digest.bytes(), digest.size()); + return digest; +} + +Hash& +Hash::hash_delimiter(string_view type) +{ + hash_buffer(HASH_DELIMITER); + hash_buffer(type); + hash_buffer(string_view("", 1)); // NUL + add_debug_text("### "); + add_debug_text(type); + add_debug_text("\n"); + return *this; +} + +Hash& +Hash::hash(const void* data, size_t size, HashType hash_type) +{ + string_view buffer(static_cast(data), size); + hash_buffer(buffer); + add_debug_text(buffer); + if (hash_type == HashType::text) { + add_debug_text("\n"); + } + return *this; +} + +Hash& +Hash::hash(nonstd::string_view data) +{ + hash(data.data(), data.length()); + return *this; +} + +Hash& +Hash::hash(int64_t x) +{ + hash_buffer(string_view(reinterpret_cast(&x), sizeof(x))); + add_debug_text(fmt::format("{}\n", x)); + return *this; +} + +bool +Hash::hash_fd(int fd) +{ + char buf[READ_BUFFER_SIZE]; + ssize_t n; + + while ((n = read(fd, buf, sizeof(buf))) != 0) { + if (n == -1 && errno != EINTR) { + break; + } + if (n > 0) { + hash_buffer(string_view(buf, n)); + add_debug_text(string_view(buf, n)); + } + } + return n >= 0; +} + +bool +Hash::hash_file(const std::string& path) +{ + Fd fd(open(path.c_str(), O_RDONLY | O_BINARY)); + if (!fd) { + cc_log("Failed to open %s: %s", path.c_str(), strerror(errno)); + return false; + } + + bool ret = hash_fd(*fd); + return ret; +} + +void +Hash::hash_buffer(string_view buffer) +{ + blake3_hasher_update(&m_hasher, buffer.data(), buffer.size()); + if (!buffer.empty() && m_debug_binary) { + (void)fwrite(buffer.data(), 1, buffer.size(), m_debug_binary); + } +} + +void +Hash::add_debug_text(string_view text) +{ + if (!text.empty() && m_debug_text) { + (void)fwrite(text.data(), 1, text.length(), m_debug_text); + } +} diff --git a/src/Hash.hpp b/src/Hash.hpp new file mode 100644 index 000000000..9c96b5cdf --- /dev/null +++ b/src/Hash.hpp @@ -0,0 +1,100 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Digest.hpp" + +#include "third_party/blake3/blake3.h" +#include "third_party/nonstd/string_view.hpp" + +// This class represents a hash state. +class Hash +{ +public: + enum class HashType { binary, text }; + + Hash(); + Hash(const Hash& other) = default; + + Hash& operator=(const Hash& other) = default; + + // Enable debug logging of the hashed input to a binary and a text file. + void enable_debug(nonstd::string_view section_name, + FILE* debug_binary, + FILE* debug_text); + + // Retrieve the digest. + Digest digest() const; + + // Hash some data that is unlikely to occur in the input. The idea is twofold: + // + // - Delimit things like arguments from each other (e.g., so that -I -O2 and + // -I-O2 hash differently). + // - Tag different types of hashed information so that it's possible to do + // conditional hashing of information in a safe way (e.g., if we want to + // hash information X if CCACHE_A is set and information Y if CCACHE_B is + // set, there should never be a hash collision risk). + Hash& hash_delimiter(nonstd::string_view type); + + // Add bytes to the hash. + // + // If hash debugging is enabled, the buffer content is written verbatim to the + // text input file, followed by a newline character if `hash_type` is + // HashType::text. + Hash& + hash(const void* data, size_t size, HashType hash_type = HashType::text); + + // Add a string to the hash. + // + // If hash debugging is enabled, the string is written to the text input file + // followed by a newline. + Hash& hash(nonstd::string_view data); + + // Add an integer to the hash. + // + // If hash debugging is enabled, the integer is written in text form to the + // text input file followed by a newline. + Hash& hash(int64_t x); + + // Add contents read from an open file descriptor to the hash. + // + // If hash debugging is enabled, the data is written verbatim to the text + // input file. + // + // Returns true on success, otherwise false. + bool hash_fd(int fd); + + // Add file contents to the hash. + // + // If hash debugging is enabled, the data is written verbatim to the text + // input file. + // + // Returns true on success, otherwise false. + bool hash_file(const std::string& path); + +private: + blake3_hasher m_hasher; + FILE* m_debug_binary = nullptr; + FILE* m_debug_text = nullptr; + + void hash_buffer(nonstd::string_view buffer); + void add_debug_text(nonstd::string_view text); +}; diff --git a/src/InodeCache.cpp b/src/InodeCache.cpp index 64cba5775..adac7cac6 100644 --- a/src/InodeCache.cpp +++ b/src/InodeCache.cpp @@ -21,10 +21,10 @@ #include "Config.hpp" #include "Fd.hpp" #include "Finalizer.hpp" +#include "Hash.hpp" #include "Stat.hpp" #include "Util.hpp" #include "ccache.hpp" -#include "hash.hpp" #include "logging.hpp" #include @@ -195,7 +195,9 @@ InodeCache::hash_inode(const char* path, ContentType type, Digest& digest) #endif key.st_size = stat.size(); - digest = hash_buffer_once(&key, sizeof(Key)); + Hash hash; + hash.hash(&key, sizeof(Key)); + digest = hash.digest(); return true; } diff --git a/src/ccache.cpp b/src/ccache.cpp index d120fddac..f7794fc99 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -24,8 +24,8 @@ #include "Context.hpp" #include "Fd.hpp" #include "File.hpp" -#include "Finalizer.hpp" #include "FormatNonstdStringView.hpp" +#include "Hash.hpp" #include "MiniTrace.hpp" #include "ProgressBar.hpp" #include "Result.hpp" @@ -41,7 +41,6 @@ #include "compress.hpp" #include "exceptions.hpp" #include "execute.hpp" -#include "hash.hpp" #include "hashutil.hpp" #include "language.hpp" #include "logging.hpp" @@ -205,7 +204,7 @@ clean_up_internal_tempdir(const Config& config) static void init_hash_debug(Context& ctx, - struct hash* hash, + Hash& hash, const char* obj_path, char type, const char* section_name, @@ -218,8 +217,7 @@ init_hash_debug(Context& ctx, std::string path = fmt::format("{}.ccache-input-{}", obj_path, type); File debug_binary_file(path, "wb"); if (debug_binary_file) { - hash_enable_debug( - hash, section_name, debug_binary_file.get(), debug_text_file); + hash.enable_debug(section_name, debug_binary_file.get(), debug_text_file); ctx.hash_debug_files.push_back(std::move(debug_binary_file)); } else { cc_log("Failed to open %s: %s", path.c_str(), strerror(errno)); @@ -247,9 +245,9 @@ guess_compiler(const char* path) static bool do_remember_include_file(Context& ctx, std::string path, - struct hash* cpp_hash, + Hash& cpp_hash, bool system, - struct hash* depend_mode_hash) + Hash* depend_mode_hash) { bool is_pch = false; @@ -326,8 +324,7 @@ do_remember_include_file(Context& ctx, } // Let's hash the include file content. - struct hash* fhash = hash_init(); - Finalizer fhash_finalizer([=] { hash_free(fhash); }); + Hash fhash; is_pch = is_precompiled_header(path.c_str()); if (is_pch) { @@ -349,8 +346,8 @@ do_remember_include_file(Context& ctx, if (!hash_binary_file(ctx, fhash, path.c_str())) { return false; } - hash_delimiter(cpp_hash, using_pch_sum ? "pch_sum_hash" : "pch_hash"); - hash_string(cpp_hash, hash_result(fhash).to_string()); + cpp_hash.hash_delimiter(using_pch_sum ? "pch_sum_hash" : "pch_hash"); + cpp_hash.hash(fhash.digest().to_string()); } if (ctx.config.direct_mode()) { @@ -362,12 +359,12 @@ do_remember_include_file(Context& ctx, } } - Digest d = hash_result(fhash); + Digest d = fhash.digest(); ctx.included_files.emplace(path, d); if (depend_mode_hash) { - hash_delimiter(depend_mode_hash, "include"); - hash_string(depend_mode_hash, d.to_string()); + depend_mode_hash->hash_delimiter("include"); + depend_mode_hash->hash(d.to_string()); } } @@ -380,9 +377,9 @@ do_remember_include_file(Context& ctx, static void remember_include_file(Context& ctx, const std::string& path, - struct hash* cpp_hash, + Hash& cpp_hash, bool system, - struct hash* depend_mode_hash) + Hash* depend_mode_hash) { if (!do_remember_include_file(ctx, path, cpp_hash, system, depend_mode_hash) && ctx.config.direct_mode()) { @@ -407,10 +404,7 @@ print_included_files(const Context& ctx, FILE* fp) // - Stores the paths and hashes of included files in the global variable // g_included_files. static bool -process_preprocessed_file(Context& ctx, - struct hash* hash, - const char* path, - bool pump) +process_preprocessed_file(Context& ctx, Hash& hash, const char* path, bool pump) { char* data; size_t size; @@ -461,7 +455,7 @@ process_preprocessed_file(Context& ctx, if (str_startswith(q, "# 31 \"\"\n")) { // Bogus extra line with #31, after the regular #1: Ignore the whole // line, and continue parsing. - hash_string_buffer(hash, p, q - p); + hash.hash(p, q - p); while (q < end && *q != '\n') { q++; } @@ -471,7 +465,7 @@ process_preprocessed_file(Context& ctx, } else if (str_startswith(q, "# 32 \"\" 2\n")) { // Bogus wrong line with #32, instead of regular #1: Replace the line // number with the usual one. - hash_string_buffer(hash, p, q - p); + hash.hash(p, q - p); q += 1; q[0] = '#'; q[1] = ' '; @@ -494,7 +488,7 @@ process_preprocessed_file(Context& ctx, return false; } // q points to the beginning of an include file path - hash_string_buffer(hash, p, q - p); + hash.hash(p, q - p); p = q; while (q < end && *q != '"') { q++; @@ -532,7 +526,7 @@ process_preprocessed_file(Context& ctx, } } if (should_hash_inc_path) { - hash_string_buffer(hash, inc_path, strlen(inc_path)); + hash.hash(inc_path); } remember_include_file(ctx, inc_path, hash, system, nullptr); @@ -566,7 +560,7 @@ process_preprocessed_file(Context& ctx, } } - hash_string_buffer(hash, p, (end - p)); + hash.hash(p, (end - p)); free(data); // Explicitly check the .gch/.pch/.pth file as Clang does not include any @@ -574,7 +568,7 @@ process_preprocessed_file(Context& ctx, if (!ctx.included_pch_file.empty()) { std::string pch_path = Util::make_relative_path(ctx, ctx.included_pch_file.c_str()); - hash_string(hash, pch_path); + hash.hash(pch_path); remember_include_file(ctx, pch_path, hash, false, nullptr); } @@ -663,7 +657,7 @@ use_relative_paths_in_depfile(const Context& ctx) // Extract the used includes from the dependency file. Note that we cannot // distinguish system headers from other includes here. static optional -result_name_from_depfile(Context& ctx, struct hash* hash) +result_name_from_depfile(Context& ctx, Hash& hash) { std::string file_content; try { @@ -683,7 +677,7 @@ result_name_from_depfile(Context& ctx, struct hash* hash) ctx.has_absolute_include_headers = Util::is_absolute_path(token); } std::string path = Util::make_relative_path(ctx, token); - remember_include_file(ctx, path, hash, false, hash); + remember_include_file(ctx, path, hash, false, &hash); } // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the @@ -691,7 +685,7 @@ result_name_from_depfile(Context& ctx, struct hash* hash) if (!ctx.included_pch_file.empty()) { std::string pch_path = Util::make_relative_path(ctx, ctx.included_pch_file.c_str()); - hash_string(hash, pch_path); + hash.hash(pch_path); remember_include_file(ctx, pch_path, hash, false, nullptr); } @@ -700,7 +694,7 @@ result_name_from_depfile(Context& ctx, struct hash* hash) print_included_files(ctx, stdout); } - return hash_result(hash); + return hash.digest(); } // Execute the compiler/preprocessor, with logic to retry without requesting @@ -819,7 +813,7 @@ static void to_cache(Context& ctx, Args& args, Args& depend_extra_args, - struct hash* depend_mode_hash) + Hash* depend_mode_hash) { args.push_back("-o"); args.push_back(ctx.args_info.output_obj); @@ -933,7 +927,8 @@ to_cache(Context& ctx, } if (ctx.config.depend_mode()) { - auto result_name = result_name_from_depfile(ctx, depend_mode_hash); + assert(depend_mode_hash); + auto result_name = result_name_from_depfile(ctx, *depend_mode_hash); if (!result_name) { failed(STATS_ERROR); } @@ -1034,7 +1029,7 @@ to_cache(Context& ctx, // Find the result name by running the compiler in preprocessor mode and // hashing the result. static Digest -get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash) +get_result_name_from_cpp(Context& ctx, Args& args, Hash& hash) { ctx.time_of_compilation = time(nullptr); @@ -1085,13 +1080,13 @@ get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash) failed(STATS_PREPROCESSOR); } - hash_delimiter(hash, "cpp"); + hash.hash_delimiter("cpp"); bool is_pump = ctx.guessed_compiler == GuessedCompiler::pump; if (!process_preprocessed_file(ctx, hash, stdout_path.c_str(), is_pump)) { failed(STATS_ERROR); } - hash_delimiter(hash, "cppstderr"); + hash.hash_delimiter("cppstderr"); if (!ctx.args_info.direct_i_file && !hash_binary_file(ctx, hash, stderr_path.c_str())) { // Somebody removed the temporary file? @@ -1114,18 +1109,18 @@ get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash) // If we are using the CPP trick, we need to remember this stderr data and // output it just before the main stderr from the compiler pass. ctx.cpp_stderr = stderr_path; - hash_delimiter(hash, "runsecondcpp"); - hash_string(hash, "false"); + hash.hash_delimiter("runsecondcpp"); + hash.hash("false"); } - return hash_result(hash); + return hash.digest(); } // Hash mtime or content of a file, or the output of a command, according to // the CCACHE_COMPILERCHECK setting. static void hash_compiler(const Context& ctx, - struct hash* hash, + Hash& hash, const Stat& st, const char* path, bool allow_command) @@ -1133,14 +1128,14 @@ hash_compiler(const Context& ctx, if (ctx.config.compiler_check() == "none") { // Do nothing. } else if (ctx.config.compiler_check() == "mtime") { - hash_delimiter(hash, "cc_mtime"); - hash_int(hash, st.size()); - hash_int(hash, st.mtime()); + hash.hash_delimiter("cc_mtime"); + hash.hash(st.size()); + hash.hash(st.mtime()); } else if (Util::starts_with(ctx.config.compiler_check(), "string:")) { - hash_delimiter(hash, "cc_hash"); - hash_string(hash, ctx.config.compiler_check().c_str() + strlen("string:")); + hash.hash_delimiter("cc_hash"); + hash.hash(ctx.config.compiler_check().c_str() + strlen("string:")); } else if (ctx.config.compiler_check() == "content" || !allow_command) { - hash_delimiter(hash, "cc_content"); + hash.hash_delimiter("cc_content"); hash_binary_file(ctx, hash, path); } else { // command string if (!hash_multicommand_output(hash, @@ -1160,7 +1155,7 @@ hash_compiler(const Context& ctx, // in PATH instead. static void hash_nvcc_host_compiler(const Context& ctx, - struct hash* hash, + Hash& hash, const Stat* ccbin_st, const char* ccbin) { @@ -1209,15 +1204,15 @@ hash_nvcc_host_compiler(const Context& ctx, static void hash_common_info(const Context& ctx, const Args& args, - struct hash* hash, + Hash& hash, const ArgsInfo& args_info) { - hash_string(hash, HASH_PREFIX); + hash.hash(HASH_PREFIX); // We have to hash the extension, as a .i file isn't treated the same by the // compiler as a .ii file. - hash_delimiter(hash, "ext"); - hash_string(hash, ctx.config.cpp_extension().c_str()); + hash.hash_delimiter("ext"); + hash.hash(ctx.config.cpp_extension().c_str()); #ifdef _WIN32 const char* ext = strrchr(args[0].c_str(), '.'); @@ -1239,9 +1234,8 @@ hash_common_info(const Context& ctx, // Also hash the compiler name as some compilers use hard links and behave // differently depending on the real name. - hash_delimiter(hash, "cc_name"); - string_view base = Util::base_name(args[0]); - hash_string_view(hash, base); + hash.hash_delimiter("cc_name"); + hash.hash(Util::base_name(args[0])); if (!(ctx.config.sloppiness() & SLOPPY_LOCALE)) { // Hash environment variables that may affect localization of compiler @@ -1251,8 +1245,8 @@ hash_common_info(const Context& ctx, for (const char** p = envvars; *p; ++p) { char* v = getenv(*p); if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); + hash.hash_delimiter(*p); + hash.hash(v); } } } @@ -1275,8 +1269,8 @@ hash_common_info(const Context& ctx, } } cc_log("Hashing CWD %s", dir_to_hash.c_str()); - hash_delimiter(hash, "cwd"); - hash_string(hash, dir_to_hash); + hash.hash_delimiter("cwd"); + hash.hash(dir_to_hash); } if (ctx.args_info.generating_dependencies || ctx.args_info.seen_split_dwarf) { @@ -1287,8 +1281,8 @@ hash_common_info(const Context& ctx, // target object filename when using -gsplit-dwarf, so hashing the object // file path will do it, although just hashing the object file base name // would be enough. - hash_delimiter(hash, "object file"); - hash_string_view(hash, ctx.args_info.output_obj); + hash.hash_delimiter("object file"); + hash.hash(ctx.args_info.output_obj); } // Possibly hash the coverage data file path. @@ -1304,14 +1298,14 @@ hash_common_info(const Context& ctx, Util::remove_extension(Util::base_name(ctx.args_info.output_obj)); std::string gcda_path = fmt::format("{}/{}.gcda", dir, stem); cc_log("Hashing coverage path %s", gcda_path.c_str()); - hash_delimiter(hash, "gcda"); - hash_string(hash, gcda_path); + hash.hash_delimiter("gcda"); + hash.hash(gcda_path); } // Possibly hash the sanitize blacklist file path. for (const auto& sanitize_blacklist : args_info.sanitize_blacklists) { cc_log("Hashing sanitize blacklist %s", sanitize_blacklist.c_str()); - hash_delimiter(hash, "sanitizeblacklist"); + hash.hash("sanitizeblacklist"); if (!hash_binary_file(ctx, hash, sanitize_blacklist.c_str())) { failed(STATS_BADEXTRAFILE); } @@ -1321,7 +1315,7 @@ hash_common_info(const Context& ctx, for (const std::string& path : Util::split_into_strings( ctx.config.extra_files_to_hash(), PATH_DELIM)) { cc_log("Hashing extra file %s", path.c_str()); - hash_delimiter(hash, "extrafile"); + hash.hash_delimiter("extrafile"); if (!hash_binary_file(ctx, hash, path.c_str())) { failed(STATS_BADEXTRAFILE); } @@ -1332,14 +1326,14 @@ hash_common_info(const Context& ctx, if (ctx.guessed_compiler == GuessedCompiler::gcc) { const char* gcc_colors = getenv("GCC_COLORS"); if (gcc_colors) { - hash_delimiter(hash, "gcccolors"); - hash_string(hash, gcc_colors); + hash.hash_delimiter("gcccolors"); + hash.hash(gcc_colors); } } } static bool -hash_profile_data_file(const Context& ctx, struct hash* hash) +hash_profile_data_file(const Context& ctx, Hash& hash) { const std::string& profile_path = ctx.args_info.profile_path; string_view base_name = Util::remove_extension(ctx.args_info.output_obj); @@ -1365,7 +1359,7 @@ hash_profile_data_file(const Context& ctx, struct hash* hash) auto st = Stat::stat(p); if (st && !st.is_directory()) { cc_log("Adding profile data %s to the hash", p.c_str()); - hash_delimiter(hash, "-fprofile-use"); + hash.hash_delimiter("-fprofile-use"); if (hash_binary_file(ctx, hash, p.c_str())) { found = true; } @@ -1395,17 +1389,17 @@ static optional calculate_result_name(Context& ctx, const Args& args, Args& preprocessor_args, - struct hash* hash, + Hash& hash, bool direct_mode) { bool found_ccbin = false; - hash_delimiter(hash, "result version"); - hash_int(hash, Result::k_version); + hash.hash_delimiter("result version"); + hash.hash(Result::k_version); if (direct_mode) { - hash_delimiter(hash, "manifest version"); - hash_int(hash, k_manifest_version); + hash.hash_delimiter("manifest version"); + hash.hash(k_manifest_version); } // clang will emit warnings for unused linker flags, so we shouldn't skip @@ -1444,18 +1438,18 @@ calculate_result_name(Context& ctx, // the value of the option from hashing but still hash the existence of the // option. if (Util::starts_with(args[i], "-fdebug-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fdebug-prefix-map="); + hash.hash_delimiter("arg"); + hash.hash("-fdebug-prefix-map="); continue; } if (Util::starts_with(args[i], "-ffile-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-ffile-prefix-map="); + hash.hash_delimiter("arg"); + hash.hash("-ffile-prefix-map="); continue; } if (Util::starts_with(args[i], "-fmacro-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fmacro-prefix-map="); + hash.hash_delimiter("arg"); + hash.hash("-fmacro-prefix-map="); continue; } @@ -1482,17 +1476,17 @@ calculate_result_name(Context& ctx, if (Util::starts_with(args[i], "-Wp,")) { if (Util::starts_with(args[i], "-Wp,-MD,") && !strchr(args[i].c_str() + 8, ',')) { - hash_string_buffer(hash, args[i].c_str(), 8); + hash.hash(args[i].c_str(), 8); continue; } else if (Util::starts_with(args[i], "-Wp,-MMD,") && !strchr(args[i].c_str() + 9, ',')) { - hash_string_buffer(hash, args[i].c_str(), 9); + hash.hash(args[i].c_str(), 9); continue; } } else if (Util::starts_with(args[i], "-MF")) { // In either case, hash the "-MF" part. - hash_delimiter(hash, "arg"); - hash_string_buffer(hash, args[i].c_str(), 3); + hash.hash_delimiter("arg"); + hash.hash(args[i].c_str(), 3); if (ctx.args_info.output_dep != "/dev/null") { bool separate_argument = (args[i].size() == 3); @@ -1517,7 +1511,7 @@ calculate_result_name(Context& ctx, if (st) { // If given an explicit specs file, then hash that file, but don't // include the path to it in the hash. - hash_delimiter(hash, "specs"); + hash.hash_delimiter("specs"); hash_compiler(ctx, hash, st, p, false); continue; } @@ -1526,7 +1520,7 @@ calculate_result_name(Context& ctx, if (Util::starts_with(args[i], "-fplugin=")) { auto st = Stat::stat(args[i].c_str() + 9, Stat::OnError::log); if (st) { - hash_delimiter(hash, "plugin"); + hash.hash_delimiter("plugin"); hash_compiler(ctx, hash, st, args[i].c_str() + 9, false); continue; } @@ -1536,7 +1530,7 @@ calculate_result_name(Context& ctx, && args[i + 2] == "-Xclang") { auto st = Stat::stat(args[i + 3], Stat::OnError::log); if (st) { - hash_delimiter(hash, "plugin"); + hash.hash_delimiter("plugin"); hash_compiler(ctx, hash, st, args[i + 3].c_str(), false); i += 3; continue; @@ -1548,7 +1542,7 @@ calculate_result_name(Context& ctx, auto st = Stat::stat(args[i + 1], Stat::OnError::log); if (st) { found_ccbin = true; - hash_delimiter(hash, "ccbin"); + hash.hash_delimiter("ccbin"); hash_nvcc_host_compiler(ctx, hash, &st, args[i + 1].c_str()); i++; continue; @@ -1556,12 +1550,12 @@ calculate_result_name(Context& ctx, } // All other arguments are included in the hash. - hash_delimiter(hash, "arg"); - hash_string(hash, args[i]); + hash.hash_delimiter("arg"); + hash.hash(args[i]); if (i + 1 < args.size() && compopt_takes_arg(args[i])) { i++; - hash_delimiter(hash, "arg"); - hash_string(hash, args[i]); + hash.hash_delimiter("arg"); + hash.hash(args[i]); } } @@ -1569,7 +1563,7 @@ calculate_result_name(Context& ctx, // it. if (ctx.args_info.generating_dependencies && ctx.args_info.output_dep == "/dev/null") { - hash_delimiter(hash, "/dev/null dependency file"); + hash.hash_delimiter("/dev/null dependency file"); } if (!found_ccbin && ctx.args_info.actual_language == "cu") { @@ -1593,8 +1587,8 @@ calculate_result_name(Context& ctx, assert(!ctx.args_info.profile_path.empty()); cc_log("Adding profile directory %s to our hash", ctx.args_info.profile_path.c_str()); - hash_delimiter(hash, "-fprofile-dir"); - hash_string(hash, ctx.args_info.profile_path); + hash.hash_delimiter("-fprofile-dir"); + hash.hash(ctx.args_info.profile_path); } if (ctx.args_info.profile_use && !hash_profile_data_file(ctx, hash)) { @@ -1604,8 +1598,8 @@ calculate_result_name(Context& ctx, // Adding -arch to hash since cpp output is affected. for (const auto& arch : ctx.args_info.arch_args) { - hash_delimiter(hash, "-arch"); - hash_string(hash, arch); + hash.hash_delimiter("-arch"); + hash.hash(arch); } optional result_name; @@ -1620,8 +1614,8 @@ calculate_result_name(Context& ctx, for (const char** p = envvars; *p; ++p) { char* v = getenv(*p); if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); + hash.hash_delimiter(*p); + hash.hash(v); } } @@ -1636,10 +1630,10 @@ calculate_result_name(Context& ctx, // - Compiling b/x.c results in a false cache hit since a/x.c and b/x.c // share manifests and a/r.h exists. // * The expansion of __FILE__ may be incorrect. - hash_delimiter(hash, "inputfile"); - hash_string(hash, ctx.args_info.input_file); + hash.hash_delimiter("inputfile"); + hash.hash(ctx.args_info.input_file); - hash_delimiter(hash, "sourcecode"); + hash.hash_delimiter("sourcecode"); int result = hash_source_code_file(ctx, hash, ctx.args_info.input_file.c_str()); if (result & HASH_SOURCE_CODE_ERROR) { @@ -1651,7 +1645,7 @@ calculate_result_name(Context& ctx, return nullopt; } - ctx.set_manifest_name(hash_result(hash)); + ctx.set_manifest_name(hash.digest()); cc_log("Looking for result name in %s", ctx.manifest_path().c_str()); MTR_BEGIN("manifest", "manifest_get"); @@ -2080,7 +2074,7 @@ do_cache_compilation(Context& ctx, const char* const* argv) ? ctx.hash_debug_files.front().get() : nullptr; - struct hash* common_hash = hash_init(); + Hash common_hash; init_hash_debug(ctx, common_hash, ctx.args_info.output_obj.c_str(), @@ -2093,7 +2087,7 @@ do_cache_compilation(Context& ctx, const char* const* argv) MTR_END("hash", "common_hash"); // Try to find the hash using the manifest. - struct hash* direct_hash = hash_copy(common_hash); + Hash direct_hash = common_hash; init_hash_debug(ctx, direct_hash, ctx.args_info.output_obj.c_str(), @@ -2142,7 +2136,7 @@ do_cache_compilation(Context& ctx, const char* const* argv) if (!ctx.config.depend_mode()) { // Find the hash using the preprocessed output. Also updates // g_included_files. - struct hash* cpp_hash = hash_copy(common_hash); + Hash cpp_hash = common_hash; init_hash_debug(ctx, cpp_hash, ctx.args_info.output_obj.c_str(), @@ -2198,8 +2192,7 @@ do_cache_compilation(Context& ctx, const char* const* argv) add_prefix(ctx, compiler_args, ctx.config.prefix_command()); // In depend_mode, extend the direct hash. - struct hash* depend_mode_hash = - ctx.config.depend_mode() ? direct_hash : nullptr; + Hash* depend_mode_hash = ctx.config.depend_mode() ? &direct_hash : nullptr; // Run real compiler, sending output to cache. MTR_BEGIN("cache", "to_cache"); @@ -2278,14 +2271,13 @@ handle_main_options(int argc, const char* const* argv) } case HASH_FILE: { - struct hash* hash = hash_init(); + Hash hash; if (str_eq(optarg, "-")) { - hash_fd(hash, STDIN_FILENO); + hash.hash_fd(STDIN_FILENO); } else { - hash_file(hash, optarg); + hash.hash_file(optarg); } - puts(hash_result(hash).to_string().c_str()); - hash_free(hash); + fmt::print("{}", hash.digest().to_string()); break; } diff --git a/src/hash.cpp b/src/hash.cpp deleted file mode 100644 index 1d7356af9..000000000 --- a/src/hash.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (C) 2002 Andrew Tridgell -// Copyright (C) 2010-2020 Joel Rosdahl and other contributors -// -// See doc/AUTHORS.adoc for a complete list of contributors. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "hash.hpp" - -#include "Fd.hpp" -#include "Util.hpp" -#include "logging.hpp" - -#include "third_party/blake3/blake3.h" - -#define HASH_DELIMITER "\000cCaChE" - -struct hash -{ - blake3_hasher hasher; - FILE* debug_binary; - FILE* debug_text; -}; - -static void -do_hash_buffer(struct hash* hash, const void* s, size_t len) -{ - assert(s); - - blake3_hasher_update(&hash->hasher, s, len); - if (len > 0 && hash->debug_binary) { - (void)fwrite(s, 1, len, hash->debug_binary); - } -} - -static void -do_debug_text(struct hash* hash, const void* s, size_t len) -{ - if (len > 0 && hash->debug_text) { - (void)fwrite(s, 1, len, hash->debug_text); - } -} - -struct hash* -hash_init() -{ - auto hash = static_cast(malloc(sizeof(struct hash))); - blake3_hasher_init(&hash->hasher); - hash->debug_binary = nullptr; - hash->debug_text = nullptr; - return hash; -} - -struct hash* -hash_copy(struct hash* hash) -{ - auto result = static_cast(malloc(sizeof(struct hash))); - result->hasher = hash->hasher; - result->debug_binary = nullptr; - result->debug_text = nullptr; - return result; -} - -void -hash_free(struct hash* hash) -{ - free(hash); -} - -void -hash_enable_debug(struct hash* hash, - const char* section_name, - FILE* debug_binary, - FILE* debug_text) -{ - hash->debug_binary = debug_binary; - hash->debug_text = debug_text; - - do_debug_text(hash, "=== ", 4); - do_debug_text(hash, section_name, strlen(section_name)); - do_debug_text(hash, " ===\n", 5); -} - -void -hash_buffer(struct hash* hash, const void* s, size_t len) -{ - do_hash_buffer(hash, s, len); - do_debug_text(hash, s, len); -} - -Digest -hash_buffer_once(const void* s, size_t len) -{ - blake3_hasher hasher; - blake3_hasher_init(&hasher); - blake3_hasher_update(&hasher, s, len); - - Digest digest; - blake3_hasher_finalize(&hasher, digest.bytes(), digest.size()); - return digest; -} - -Digest -hash_result(struct hash* hash) -{ - // Note that blake3_hasher_finalize doesn't modify the hasher itself, thus it - // is possible to finalize again after more data has been added. - Digest digest; - blake3_hasher_finalize(&hash->hasher, digest.bytes(), digest.size()); - return digest; -} - -void -hash_delimiter(struct hash* hash, const char* type) -{ - do_hash_buffer(hash, HASH_DELIMITER, sizeof(HASH_DELIMITER)); - do_hash_buffer(hash, type, strlen(type) + 1); // Include NUL. - do_debug_text(hash, "### ", 4); - do_debug_text(hash, type, strlen(type)); - do_debug_text(hash, "\n", 1); -} - -void -hash_string(struct hash* hash, const char* s) -{ - hash_string_buffer(hash, s, strlen(s)); -} - -void -hash_string(struct hash* hash, const std::string& s) -{ - hash_string_buffer(hash, s.data(), s.length()); -} - -void -hash_string_view(struct hash* hash, nonstd::string_view sv) -{ - hash_string_buffer(hash, sv.data(), sv.length()); -} - -void -hash_string_buffer(struct hash* hash, const char* s, size_t length) -{ - hash_buffer(hash, s, length); - do_debug_text(hash, "\n", 1); -} - -void -hash_int(struct hash* hash, int x) -{ - do_hash_buffer(hash, reinterpret_cast(&x), sizeof(x)); - - char buf[16]; - snprintf(buf, sizeof(buf), "%d", x); - do_debug_text(hash, buf, strlen(buf)); - do_debug_text(hash, "\n", 1); -} - -bool -hash_fd(struct hash* hash, int fd) -{ - char buf[READ_BUFFER_SIZE]; - ssize_t n; - - while ((n = read(fd, buf, sizeof(buf))) != 0) { - if (n == -1 && errno != EINTR) { - break; - } - if (n > 0) { - do_hash_buffer(hash, buf, n); - do_debug_text(hash, buf, n); - } - } - return n >= 0; -} - -bool -hash_file(struct hash* hash, const char* fname) -{ - Fd fd(open(fname, O_RDONLY | O_BINARY)); - if (!fd) { - cc_log("Failed to open %s: %s", fname, strerror(errno)); - return false; - } - - bool ret = hash_fd(hash, *fd); - return ret; -} diff --git a/src/hash.hpp b/src/hash.hpp deleted file mode 100644 index c5781fbe8..000000000 --- a/src/hash.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2018-2020 Joel Rosdahl and other contributors -// -// See doc/AUTHORS.adoc for a complete list of contributors. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#pragma once - -#include "system.hpp" - -#include "Digest.hpp" - -#include "third_party/nonstd/string_view.hpp" - -// struct hash represents the hash algorithm's inner state. -struct hash; - -// Create a new hash state. -struct hash* hash_init(); - -// Create a new hash state from an existing hash state. -struct hash* hash_copy(struct hash* hash); - -// Free a hash state created by hash_init or hash_copy. -void hash_free(struct hash* hash); - -// Enable debug logging of hashed input to a binary and a text file. -void hash_enable_debug(struct hash* hash, - const char* section_name, - FILE* debug_binary, - FILE* debug_text); - -// Retrieve the digest. -Digest hash_result(struct hash* hash); - -// Hash some data that is unlikely to occur in the input. The idea is twofold: -// -// - Delimit things like arguments from each other (e.g., so that -I -O2 and -// -I-O2 hash differently). -// - Tag different types of hashed information so that it's possible to do -// conditional hashing of information in a safe way (e.g., if we want to hash -// information X if CCACHE_A is set and information Y if CCACHE_B is set, -// there should never be a hash collision risk). -void hash_delimiter(struct hash* hash, const char* type); - -// Hash bytes in a buffer. -// -// If hash debugging is enabled, the bytes are written verbatim to the text -// input file. -void hash_buffer(struct hash* hash, const void* s, size_t len); - -// Hash bytes in a buffer. -// -// Returns the digest. -Digest hash_buffer_once(const void* s, size_t len); - -// Hash a NUL terminated string. -// -// If hash debugging is enabled, the string is written to the text input file -// followed by a newline. -void hash_string(struct hash* hash, const char* s); - -// Hash a string with a known size. -// -// If hash debugging is enabled, the string is written to the text input file -// followed by a newline. -void hash_string_buffer(struct hash* hash, const char* s, size_t length); -void hash_string(struct hash* hash, const std::string& s); -void hash_string_view(struct hash* hash, nonstd::string_view sv); - -// Hash an integer. -// -// If hash debugging is enabled, the integer is written in text form to the -// text input file followed by a newline. -void hash_int(struct hash* hash, int x); - -// Add contents of an open file to the hash. -// -// If hash debugging is enabled, the data is written verbatim to the text input -// file. -// -// Returns true on success, otherwise false. -bool hash_fd(struct hash* hash, int fd); - -// Add contents of a file to the hash. -// -// If hash debugging is enabled, the data is written verbatim to the text input -// file. -// -// Returns true on success, otherwise false. -bool hash_file(struct hash* hash, const char* fname); diff --git a/src/hashutil.cpp b/src/hashutil.cpp index 90bfe3ba5..295c31468 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -21,6 +21,7 @@ #include "Args.hpp" #include "Config.hpp" #include "Context.hpp" +#include "Hash.hpp" #include "Stat.hpp" #include "ccache.hpp" #include "execute.hpp" @@ -192,11 +193,8 @@ check_for_temporal_macros(const char* str, size_t len) // Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results. int -hash_source_code_string(const Context& ctx, - struct hash* hash, - const char* str, - size_t len, - const char* path) +hash_source_code_string( + const Context& ctx, Hash& hash, const char* str, size_t len, const char* path) { int result = HASH_SOURCE_CODE_OK; @@ -207,7 +205,7 @@ hash_source_code_string(const Context& ctx, } // Hash the source string. - hash_string_buffer(hash, str, len); + hash.hash(str, len); if (result & HASH_SOURCE_CODE_FOUND_DATE) { cc_log("Found __DATE__ in %s", path); @@ -216,13 +214,13 @@ hash_source_code_string(const Context& ctx, // __DATE__ changes. time_t t = time(nullptr); struct tm now; - hash_delimiter(hash, "date"); + hash.hash_delimiter("date"); if (!localtime_r(&t, &now)) { return HASH_SOURCE_CODE_ERROR; } - hash_int(hash, now.tm_year); - hash_int(hash, now.tm_mon); - hash_int(hash, now.tm_mday); + hash.hash(now.tm_year); + hash.hash(now.tm_mon); + hash.hash(now.tm_mday); } if (result & HASH_SOURCE_CODE_FOUND_TIME) { // We don't know for sure that the program actually uses the __TIME__ @@ -245,7 +243,7 @@ hash_source_code_string(const Context& ctx, time_t t = stat.mtime(); tm modified; - hash_delimiter(hash, "timestamp"); + hash.hash_delimiter("timestamp"); if (!localtime_r(&t, &modified)) { return HASH_SOURCE_CODE_ERROR; } @@ -259,7 +257,7 @@ hash_source_code_string(const Context& ctx, if (!timestamp) { return HASH_SOURCE_CODE_ERROR; } - hash_string(hash, timestamp); + hash.hash(timestamp); } return result; @@ -267,13 +265,13 @@ hash_source_code_string(const Context& ctx, static int hash_source_code_file_nocache(const Context& ctx, - struct hash* hash, + Hash& hash, const char* path, size_t size_hint, bool is_precompiled) { if (is_precompiled) { - if (hash_file(hash, path)) { + if (hash.hash_file(path)) { return HASH_SOURCE_CODE_OK; } else { return HASH_SOURCE_CODE_ERROR; @@ -308,7 +306,7 @@ get_content_type(const Config& config, const char* path) // results. int hash_source_code_file(const Context& ctx, - struct hash* hash, + Hash& hash, const char* path, size_t size_hint) { @@ -328,7 +326,7 @@ hash_source_code_file(const Context& ctx, Digest digest; int return_value; if (!ctx.inode_cache.get(path, content_type, digest, &return_value)) { - struct hash* file_hash = hash_init(); + Hash file_hash; return_value = hash_source_code_file_nocache( ctx, file_hash, @@ -338,11 +336,10 @@ hash_source_code_file(const Context& ctx, if (return_value == HASH_SOURCE_CODE_ERROR) { return HASH_SOURCE_CODE_ERROR; } - digest = hash_result(file_hash); - hash_free(file_hash); + digest = file_hash.digest(); ctx.inode_cache.put(path, content_type, digest, return_value); } - hash_buffer(hash, digest.bytes(), Digest::size()); + hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary); return return_value; #endif } @@ -351,10 +348,10 @@ hash_source_code_file(const Context& ctx, // // Returns true on success, otherwise false. bool -hash_binary_file(const Context& ctx, struct hash* hash, const char* path) +hash_binary_file(const Context& ctx, Hash& hash, const char* path) { if (!ctx.config.inode_cache()) { - return hash_file(hash, path); + return hash.hash_file(path); } #ifdef INODE_CACHE_SUPPORTED @@ -363,25 +360,22 @@ hash_binary_file(const Context& ctx, struct hash* hash, const char* path) // add the digest into the outer hash instead. Digest digest; if (!ctx.inode_cache.get(path, InodeCache::ContentType::binary, digest)) { - struct hash* file_hash = hash_init(); - if (!hash_file(file_hash, path)) { + Hash file_hash; + if (!file_hash.hash_file(path)) { return false; } - digest = hash_result(file_hash); - hash_free(file_hash); + digest = file_hash.digest(); ctx.inode_cache.put(path, InodeCache::ContentType::binary, digest); } - hash_buffer(hash, digest.bytes(), Digest::size()); + hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary); return true; #else - return hash_file(hash, path); + return hash.hash_file(path); #endif } bool -hash_command_output(struct hash* hash, - const char* command, - const char* compiler) +hash_command_output(Hash& hash, const char* command, const char* compiler) { #ifdef _WIN32 // Trim leading space. @@ -461,7 +455,7 @@ hash_command_output(struct hash* hash, return false; } int fd = _open_osfhandle((intptr_t)pipe_out[0], O_BINARY); - bool ok = hash_fd(hash, fd); + bool ok = hash.hash_fd(fd); if (!ok) { cc_log("Error hashing compiler check command output: %s", strerror(errno)); } @@ -498,7 +492,7 @@ hash_command_output(struct hash* hash, } else { // Parent. close(pipefd[1]); - bool ok = hash_fd(hash, pipefd[0]); + bool ok = hash.hash_fd(pipefd[0]); if (!ok) { cc_log("Error hashing compiler check command output: %s", strerror(errno)); @@ -520,9 +514,7 @@ hash_command_output(struct hash* hash, } bool -hash_multicommand_output(struct hash* hash, - const char* commands, - const char* compiler) +hash_multicommand_output(Hash& hash, const char* commands, const char* compiler) { bool ok = true; for (const std::string& cmd : Util::split_into_strings(commands, ";")) { diff --git a/src/hashutil.hpp b/src/hashutil.hpp index 5350b9a0e..5b4acd5ec 100644 --- a/src/hashutil.hpp +++ b/src/hashutil.hpp @@ -20,12 +20,11 @@ #include "system.hpp" -#include "hash.hpp" - #include class Config; class Context; +class Hash; unsigned hash_from_int(int i); @@ -37,18 +36,15 @@ unsigned hash_from_int(int i); int check_for_temporal_macros(const char* str, size_t len); int hash_source_code_string(const Context& ctx, - struct hash* hash, + Hash& hash, const char* str, size_t len, const char* path); int hash_source_code_file(const Context& ctx, - struct hash* hash, + Hash& hash, const char* path, size_t size_hint = 0); -bool hash_binary_file(const Context& ctx, struct hash* hash, const char* path); -bool hash_command_output(struct hash* hash, - const char* command, - const char* compiler); -bool hash_multicommand_output(struct hash* hash, - const char* command, - const char* compiler); +bool hash_binary_file(const Context& ctx, Hash& hash, const char* path); +bool hash_command_output(Hash& hash, const char* command, const char* compiler); +bool +hash_multicommand_output(Hash& hash, const char* command, const char* compiler); diff --git a/src/manifest.cpp b/src/manifest.cpp index 87abeb938..9bfa51210 100644 --- a/src/manifest.cpp +++ b/src/manifest.cpp @@ -26,9 +26,9 @@ #include "Context.hpp" #include "Digest.hpp" #include "File.hpp" +#include "Hash.hpp" #include "StdMakeUnique.hpp" #include "ccache.hpp" -#include "hash.hpp" #include "hashutil.hpp" #include "logging.hpp" @@ -450,20 +450,17 @@ verify_result(const Context& ctx, auto hashed_files_iter = hashed_files.find(path); if (hashed_files_iter == hashed_files.end()) { - struct hash* hash = hash_init(); + Hash hash; int ret = hash_source_code_file(ctx, hash, path.c_str(), fs.size); if (ret & HASH_SOURCE_CODE_ERROR) { cc_log("Failed hashing %s", path.c_str()); - hash_free(hash); return false; } if (ret & HASH_SOURCE_CODE_FOUND_TIME) { - hash_free(hash); return false; } - Digest actual = hash_result(hash); - hash_free(hash); + Digest actual = hash.digest(); hashed_files_iter = hashed_files.emplace(path, actual).first; } diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 446a59452..c791a6703 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -8,6 +8,7 @@ set( test_Compression.cpp test_Config.cpp test_FormatNonstdStringView.cpp + test_Hash.cpp test_Lockfile.cpp test_NullCompression.cpp test_Stat.cpp @@ -15,7 +16,6 @@ set( test_ZstdCompression.cpp test_argprocessing.cpp test_compopt.cpp - test_hash.cpp test_hashutil.cpp test_legacy_util.cpp) diff --git a/unittest/test_hash.cpp b/unittest/test_Hash.cpp similarity index 50% rename from unittest/test_hash.cpp rename to unittest/test_Hash.cpp index 3e996554f..c1a864457 100644 --- a/unittest/test_hash.cpp +++ b/unittest/test_Hash.cpp @@ -16,90 +16,72 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "../src/hash.hpp" +#include "../src/Hash.hpp" #include "third_party/doctest.h" -TEST_SUITE_BEGIN("hash"); +TEST_SUITE_BEGIN("Hash"); -TEST_CASE("test_known_strings") +TEST_CASE("known strings") { + SUBCASE("initial state") { - struct hash* h = hash_init(); - hash_string(h, ""); - CHECK(hash_result(h).to_string() + CHECK(Hash().digest().to_string() == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); - hash_free(h); } + SUBCASE("empty string") { - struct hash* h = hash_init(); - hash_string(h, "a"); - CHECK(hash_result(h).to_string() + CHECK(Hash().hash("").digest().to_string() + == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); + } + + SUBCASE("a") + { + CHECK(Hash().hash("a").digest().to_string() == "17762fddd969a453925d65717ac3eea21320b66b"); - hash_free(h); } + SUBCASE("message digest") { - struct hash* h = hash_init(); - hash_string(h, "message digest"); - CHECK(hash_result(h).to_string() + CHECK(Hash().hash("message digest").digest().to_string() == "7bc2a2eeb95ddbf9b7ecf6adcb76b453091c58dc"); - hash_free(h); } + SUBCASE("long string") { - struct hash* h = hash_init(); - hash_string( - h, - "1234567890123456789012345678901234567890123456789012345678901234567890" - "1234567890"); - CHECK(hash_result(h).to_string() + const char long_string[] = + "123456789012345678901234567890123456789012345678901234567890" + "12345678901234567890"; + CHECK(Hash().hash(long_string).digest().to_string() == "f263acf51621980b9c8de5da4a17d314984e05ab"); - hash_free(h); } } -TEST_CASE("hash_result_should_not_alter_state") +TEST_CASE("Hash::digest should not alter state") { - struct hash* h = hash_init(); - hash_string(h, "message"); - hash_result(h); - hash_string(h, " digest"); - CHECK(hash_result(h).to_string() - == "7bc2a2eeb95ddbf9b7ecf6adcb76b453091c58dc"); - hash_free(h); + Hash h; + h.hash("message"); + h.digest(); + h.hash(" digest"); + CHECK(h.digest().to_string() == "7bc2a2eeb95ddbf9b7ecf6adcb76b453091c58dc"); } -TEST_CASE("hash_result_should_be_idempotent") +TEST_CASE("Hash::digest should be idempotent") { - struct hash* h = hash_init(); - hash_string(h, ""); - hash_result(h); - CHECK(hash_result(h).to_string() - == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); - CHECK(hash_result(h).to_string() - == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); - hash_free(h); + Hash h; + CHECK(h.digest().to_string() == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); + CHECK(h.digest().to_string() == "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9"); } -TEST_CASE("hash_result digest bytes") +TEST_CASE("Digest::bytes") { - struct hash* h = hash_init(); - hash_string(h, "message digest"); - Digest d = hash_result(h); + Digest d = Hash().hash("message digest").digest(); uint8_t expected[Digest::size()] = { 0x7b, 0xc2, 0xa2, 0xee, 0xb9, 0x5d, 0xdb, 0xf9, 0xb7, 0xec, 0xf6, 0xad, 0xcb, 0x76, 0xb4, 0x53, 0x09, 0x1c, 0x58, 0xdc, }; CHECK(memcmp(d.bytes(), expected, sizeof(Digest::size())) == 0); - hash_free(h); -} - -TEST_CASE("hash_once") -{ - CHECK(hash_buffer_once("a", 1).to_string() - == "17762fddd969a453925d65717ac3eea21320b66b"); } TEST_SUITE_END(); diff --git a/unittest/test_InodeCache.cpp b/unittest/test_InodeCache.cpp index 38fa752fb..b158c7537 100644 --- a/unittest/test_InodeCache.cpp +++ b/unittest/test_InodeCache.cpp @@ -18,9 +18,9 @@ #include "../src/Config.hpp" #include "../src/Context.hpp" +#include "../src/Hash.hpp" #include "../src/InodeCache.hpp" #include "../src/Util.hpp" -#include "../src/hash.hpp" #include "TestUtil.hpp" #include "third_party/doctest.h" @@ -29,29 +29,12 @@ using TestUtil::TestContext; namespace { -Digest -digest_from_string(const char* s) -{ - Digest digest; - struct hash* hash = hash_init(); - hash_string(hash, s); - digest = hash_result(hash); - hash_free(hash); - return digest; -} - -bool -digest_equals_string(const Digest& digest, const char* s) -{ - return digest == digest_from_string(s); -} - bool put(const Context& ctx, const char* filename, const char* s, int return_value) { return ctx.inode_cache.put(filename, InodeCache::ContentType::code, - digest_from_string(s), + Hash().hash(s).digest(), return_value); } @@ -116,7 +99,7 @@ TEST_CASE("Test put and lookup") CHECK(ctx.inode_cache.get( "a", InodeCache::ContentType::code, digest, &return_value)); - CHECK(digest_equals_string(digest, "a text")); + CHECK(digest == Hash().hash("a text").digest()); CHECK(return_value == 1); CHECK(ctx.inode_cache.get_hits() == 1); CHECK(ctx.inode_cache.get_misses() == 0); @@ -134,7 +117,7 @@ TEST_CASE("Test put and lookup") CHECK(ctx.inode_cache.get( "a", InodeCache::ContentType::code, digest, &return_value)); - CHECK(digest_equals_string(digest, "something else")); + CHECK(digest == Hash().hash("something else").digest()); CHECK(return_value == 2); CHECK(ctx.inode_cache.get_hits() == 2); CHECK(ctx.inode_cache.get_misses() == 1); @@ -167,10 +150,10 @@ TEST_CASE("Test content type") ctx.inode_cache.drop(); ctx.config.set_inode_cache(true); Util::write_file("a", "a text"); - Digest binary_digest = digest_from_string("binary"); - Digest code_digest = digest_from_string("code"); + Digest binary_digest = Hash().hash("binary").digest(); + Digest code_digest = Hash().hash("code").digest(); Digest code_with_sloppy_time_macros_digest = - digest_from_string("sloppy_time_macros"); + Hash().hash("sloppy_time_macros").digest(); CHECK(ctx.inode_cache.put( "a", InodeCache::ContentType::binary, binary_digest, 1)); diff --git a/unittest/test_hashutil.cpp b/unittest/test_hashutil.cpp index 89cb59a9c..9ca6678bb 100644 --- a/unittest/test_hashutil.cpp +++ b/unittest/test_hashutil.cpp @@ -17,6 +17,7 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "../src/Context.hpp" +#include "../src/Hash.hpp" #include "../src/hashutil.hpp" #include "TestUtil.hpp" @@ -28,62 +29,50 @@ TEST_SUITE_BEGIN("hashutil"); TEST_CASE("hash_command_output_simple") { - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; CHECK(hash_command_output(h1, "echo", "not used")); CHECK(hash_command_output(h2, "echo", "not used")); - CHECK(hash_result(h1) == hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_command_output_space_removal") { - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; CHECK(hash_command_output(h1, "echo", "not used")); CHECK(hash_command_output(h2, " echo ", "not used")); - CHECK(hash_result(h1) == hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_command_output_hash_inequality") { - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; CHECK(hash_command_output(h1, "echo foo", "not used")); CHECK(hash_command_output(h2, "echo bar", "not used")); - CHECK(hash_result(h1) != hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() != h2.digest()); } TEST_CASE("hash_command_output_compiler_substitution") { - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; CHECK(hash_command_output(h1, "echo foo", "not used")); CHECK(hash_command_output(h2, "%compiler% foo", "echo")); - CHECK(hash_result(h1) == hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_command_output_stdout_versus_stderr") { TestContext test_context; - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; #ifndef _WIN32 Util::write_file("stderr.sh", "#!/bin/sh\necho foo >&2\n"); @@ -95,16 +84,13 @@ TEST_CASE("hash_command_output_stdout_versus_stderr") CHECK(hash_command_output(h1, "echo foo", "not used")); CHECK(hash_command_output(h2, "stderr.bat", "not used")); #endif - CHECK(hash_result(h1) == hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_multicommand_output") { - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; #ifndef _WIN32 Util::write_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n"); @@ -116,23 +102,17 @@ TEST_CASE("hash_multicommand_output") CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); CHECK(hash_multicommand_output(h1, "foo.bat", "not used")); #endif - CHECK(hash_result(h1) == hash_result(h2)); - - hash_free(h2); - hash_free(h1); + CHECK(h1.digest() == h2.digest()); } TEST_CASE("hash_multicommand_output_error_handling") { Context ctx; - struct hash* h1 = hash_init(); - struct hash* h2 = hash_init(); + Hash h1; + Hash h2; CHECK(!hash_multicommand_output(h2, "false; true", "not used")); - - hash_free(h2); - hash_free(h1); } TEST_CASE("check_for_temporal_macros")