Context.cpp
Counters.cpp
Decompressor.cpp
+ Hash.cpp
Lockfile.cpp
MiniTrace.cpp
NullCompressor.cpp
compopt.cpp
compress.cpp
execute.cpp
- hash.cpp
hashutil.cpp
language.cpp
legacy_util.cpp
#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"
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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);
+};
#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>
#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;
}
#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"
#include "compress.hpp"
#include "exceptions.hpp"
#include "execute.hpp"
-#include "hash.hpp"
#include "hashutil.hpp"
#include "language.hpp"
#include "logging.hpp"
static void
init_hash_debug(Context& ctx,
- struct hash* hash,
+ Hash& hash,
const char* obj_path,
char type,
const char* section_name,
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));
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;
}
// 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) {
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()) {
}
}
- 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());
}
}
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()) {
// - 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;
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++;
}
} 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] = ' ';
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++;
}
}
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);
}
}
- 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
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);
}
// 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 {
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
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);
}
print_included_files(ctx, stdout);
}
- return hash_result(hash);
+ return hash.digest();
}
// Execute the compiler/preprocessor, with logic to retry without requesting
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);
}
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);
}
// 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);
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?
// 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)
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,
// in PATH instead.
static void
hash_nvcc_host_compiler(const Context& ctx,
- struct hash* hash,
+ Hash& hash,
const Stat* ccbin_st,
const char* ccbin)
{
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(), '.');
// 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
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);
}
}
}
}
}
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) {
// 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.
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);
}
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);
}
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);
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;
}
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
// 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;
}
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);
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;
}
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;
}
&& 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;
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;
}
// 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]);
}
}
// 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") {
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)) {
// 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;
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);
}
}
// - 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) {
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");
? 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(),
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(),
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(),
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");
}
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;
}
+++ /dev/null
-// 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;
-}
+++ /dev/null
-// 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);
#include "Args.hpp"
#include "Config.hpp"
#include "Context.hpp"
+#include "Hash.hpp"
#include "Stat.hpp"
#include "ccache.hpp"
#include "execute.hpp"
// 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;
}
// 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);
// __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__
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;
}
if (!timestamp) {
return HASH_SOURCE_CODE_ERROR;
}
- hash_string(hash, timestamp);
+ hash.hash(timestamp);
}
return result;
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;
// results.
int
hash_source_code_file(const Context& ctx,
- struct hash* hash,
+ Hash& hash,
const char* path,
size_t size_hint)
{
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,
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
}
//
// 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
// 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.
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));
}
} 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));
}
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, ";")) {
#include "system.hpp"
-#include "hash.hpp"
-
#include <inttypes.h>
class Config;
class Context;
+class Hash;
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);
#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"
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;
}
test_Compression.cpp
test_Config.cpp
test_FormatNonstdStringView.cpp
+ test_Hash.cpp
test_Lockfile.cpp
test_NullCompression.cpp
test_Stat.cpp
test_ZstdCompression.cpp
test_argprocessing.cpp
test_compopt.cpp
- test_hash.cpp
test_hashutil.cpp
test_legacy_util.cpp)
// 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();
#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"
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);
}
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);
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);
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));
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "../src/Context.hpp"
+#include "../src/Hash.hpp"
#include "../src/hashutil.hpp"
#include "TestUtil.hpp"
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");
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");
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")