]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
C++-ify struct hash
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 8 Jul 2020 07:51:10 +0000 (09:51 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Thu, 9 Jul 2020 18:22:52 +0000 (20:22 +0200)
15 files changed:
src/CMakeLists.txt
src/Context.hpp
src/Hash.cpp [new file with mode: 0644]
src/Hash.hpp [new file with mode: 0644]
src/InodeCache.cpp
src/ccache.cpp
src/hash.cpp [deleted file]
src/hash.hpp [deleted file]
src/hashutil.cpp
src/hashutil.hpp
src/manifest.cpp
unittest/CMakeLists.txt
unittest/test_Hash.cpp [moved from unittest/test_hash.cpp with 50% similarity]
unittest/test_InodeCache.cpp
unittest/test_hashutil.cpp

index 29838355edb8fc95f2a97ae478b67bf276b2144d..1d3d25ed67c51290a4dc0f213a614f4cab923e5d 100644 (file)
@@ -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
index 8e6c332e77df0d10657e320de45c874935d8f876..2ab6052cec411301e16c8ffd0fc5a0a3962889eb 100644 (file)
 #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 (file)
index 0000000..2812c2d
--- /dev/null
@@ -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<const char*>(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<const char*>(&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 (file)
index 0000000..9c96b5c
--- /dev/null
@@ -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);
+};
index 64cba57756a16cf76c51fdcf4617bf3bbfb79511..adac7cac6701368df11944a4545d85802ea0a182 100644 (file)
 #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 <atomic>
@@ -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;
 }
 
index d120fddac9bd8989afcf24fbecd179ce1ae90b7b..f7794fc995bfdc329713a5856309468b2a40b841 100644 (file)
@@ -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 \"<command-line>\"\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 \"<command-line>\" 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<Digest>
-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<Digest>
 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<Digest> 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 (file)
index 1d7356a..0000000
+++ /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<struct hash*>(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<struct hash*>(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<const char*>(&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 (file)
index c5781fb..0000000
+++ /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);
index 90bfe3ba54c271736ec1dbf09c2a1477e00f17e6..295c31468f7846e5b2dcc3f80fd499c20f250a0c 100644 (file)
@@ -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, ";")) {
index 5350b9a0ee99e205bf8e11e4333750aa7e509022..5b4acd5ecbd81f0be93de3cc4aba01c045f4bafd 100644 (file)
 
 #include "system.hpp"
 
-#include "hash.hpp"
-
 #include <inttypes.h>
 
 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);
index 87abeb938f38bd9e06587984b0473e0a937a1c73..9bfa5121029f41afe579a170073a07b93e7cb791 100644 (file)
@@ -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;
     }
 
index 446a59452a6d9f8ca0395dcb54d0e2ee7b49d082..c791a6703cef66d987531892daef255c3b810633 100644 (file)
@@ -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)
 
similarity index 50%
rename from unittest/test_hash.cpp
rename to unittest/test_Hash.cpp
index 3e996554faa2f29c8cbbffb961ddef48e945c268..c1a864457e7e0565aab1d9314af090e5e3683aa1 100644 (file)
 // 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();
index 38fa752fbe935d39c1f46a079e4ccac7d159c846..b158c7537af2947ee3e05be88411210993a0db37 100644 (file)
@@ -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));
index 89cb59a9c8a752911bd0363ca909e0b83ad97b35..9ca6678bb769152e10a50f2961fc8120e975052d 100644 (file)
@@ -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")