}
void
-Context::set_manifest_name(const struct digest& name)
+Context::set_manifest_name(const Digest& name)
{
m_manifest_name = name;
set_path_and_stats_file(
}
void
-Context::set_result_name(const struct digest& name)
+Context::set_result_name(const Digest& name)
{
m_result_name = name;
set_path_and_stats_file(name, ".result", m_result_path, m_result_stats_file);
}
void
-Context::set_path_and_stats_file(const struct digest& name,
+Context::set_path_and_stats_file(const Digest& name,
string_view suffix,
std::string& path_var,
std::string& stats_file_var)
{
- char name_string[DIGEST_STRING_BUFFER_SIZE];
- digest_as_string(&name, name_string);
+ std::string name_string = name.to_string();
path_var = Util::get_path_in_cache(
config.cache_dir(), config.cache_dir_levels(), name_string, suffix);
stats_file_var =
// The original argument list.
Args orig_args;
- // Name (represented as a struct digest) of the file containing the cached
- // result.
- const struct digest& result_name() const;
+ // Name (represented as a hash) of the file containing the cached result.
+ const Digest& result_name() const;
// Full path to the file containing the result
// (cachedir/a/b/cdef[...]-size.result).
time_t time_of_compilation = 0;
// Files included by the preprocessor and their hashes.
- // Key: file path. Value: struct digest.
- std::unordered_map<std::string, digest> included_files;
+ std::unordered_map<std::string, Digest> included_files;
// Uses absolute path for some include files.
bool has_absolute_include_headers = false;
// The name of the cpp stderr file.
std::string cpp_stderr;
- // Name (represented as a struct digest) of the file containing the manifest
- // for the cached result.
- const struct digest& manifest_name() const;
+ // Name (represented as a hash) of the file containing the manifest for the
+ // cached result.
+ const Digest& manifest_name() const;
// The stats file to use for the manifest.
const std::string& manifest_stats_file() const;
std::unique_ptr<MiniTrace> mini_trace;
#endif
- void set_manifest_name(const struct digest& name);
- void set_result_name(const struct digest& name);
+ void set_manifest_name(const Digest& name);
+ void set_result_name(const Digest& name);
// Register a temporary file to remove at program exit.
void register_pending_tmp_file(const std::string& path);
private:
- nonstd::optional<struct digest> m_manifest_name;
+ nonstd::optional<Digest> m_manifest_name;
std::string m_manifest_path;
std::string m_manifest_stats_file;
- nonstd::optional<struct digest> m_result_name;
+ nonstd::optional<Digest> m_result_name;
std::string m_result_path;
mutable std::string m_result_stats_file;
void unlink_pending_tmp_files();
void unlink_pending_tmp_files_signal_safe(); // called from signal handler
- void set_path_and_stats_file(const struct digest& name,
+ void set_path_and_stats_file(const Digest& name,
nonstd::string_view suffix,
std::string& path_var,
std::string& stats_file_var);
};
-inline const struct digest&
+inline const Digest&
Context::manifest_name() const
{
return *m_manifest_name;
return m_manifest_stats_file;
}
-inline const struct digest&
+inline const Digest&
Context::result_name() const
{
return *m_result_name;
--- /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 "Util.hpp"
+
+#include "third_party/fmt/core.h"
+
+#include <string>
+
+// Digest represents the binary form of the final digest (AKA hash or checksum)
+// produced by the hash algorithm.
+class Digest
+{
+public:
+ Digest() = default;
+
+ // Access the raw byte buffer, which is `size()` bytes long.
+ const uint8_t* bytes() const;
+ uint8_t* bytes();
+
+ // Size of the digest in bytes.
+ constexpr static size_t size();
+
+ // Format the digest as hex string.
+ std::string to_string() const;
+
+ bool operator==(const Digest& other) const;
+ bool operator!=(const Digest& other) const;
+
+private:
+ constexpr static size_t k_digest_size = 20;
+ uint8_t m_bytes[k_digest_size];
+};
+
+inline const uint8_t*
+Digest::bytes() const
+{
+ return m_bytes;
+}
+
+inline uint8_t*
+Digest::bytes()
+{
+ return m_bytes;
+}
+
+inline constexpr size_t
+Digest::size()
+{
+ return k_digest_size;
+}
+
+inline std::string
+Digest::to_string() const
+{
+ return Util::format_hex(m_bytes, size());
+}
+
+inline bool
+Digest::operator==(const Digest& other) const
+{
+ return memcmp(bytes(), other.bytes(), size()) == 0;
+}
+
+inline bool
+Digest::operator!=(const Digest& other) const
+{
+ return !(*this == other);
+}
# include <sys/stat.h>
# include <sys/types.h>
# include <time.h>
+# include <type_traits>
# include <unistd.h>
namespace {
const uint32_t k_num_buckets = 32 * 1024;
const uint32_t k_num_entries = 4;
-static_assert(sizeof(digest::bytes) == 20,
+static_assert(Digest::size() == 20,
"Increment version number if size of digest is changed.");
+static_assert(std::is_trivially_copyable<Digest>::value,
+ "Digest is expected to be trivially copyable.");
static_assert(
static_cast<int>(InodeCache::ContentType::binary) == 0,
struct InodeCache::Entry
{
- digest key_digest; // Hashed key
- digest file_digest; // Cached file hash
+ Digest key_digest; // Hashed key
+ Digest file_digest; // Cached file hash
int return_value; // Cached return value
};
}
bool
-InodeCache::hash_inode(const char* path, ContentType type, digest* digest)
+InodeCache::hash_inode(const char* path, ContentType type, Digest& digest)
{
Stat stat = Stat::stat(path);
if (!stat) {
struct hash* hash = hash_init();
hash_buffer(hash, &key, sizeof(Key));
- hash_result_as_bytes(hash, digest);
+ digest = hash_result(hash);
hash_free(hash);
return true;
}
}
InodeCache::Bucket*
-InodeCache::acquire_bucket(const digest& key_digest)
+InodeCache::acquire_bucket(const Digest& key_digest)
{
uint32_t hash;
- Util::big_endian_to_int(key_digest.bytes, hash);
+ Util::big_endian_to_int(key_digest.bytes(), hash);
return acquire_bucket(hash % k_num_buckets);
}
bool
InodeCache::get(const char* path,
ContentType type,
- digest* file_digest,
+ Digest& file_digest,
int* return_value)
{
if (!initialize()) {
return false;
}
- digest key_digest;
- if (!hash_inode(path, type, &key_digest)) {
+ Digest key_digest;
+ if (!hash_inode(path, type, key_digest)) {
return false;
}
bool found = false;
for (uint32_t i = 0; i < k_num_entries; ++i) {
- if (digests_equal(&bucket->entries[i].key_digest, &key_digest)) {
+ if (bucket->entries[i].key_digest == key_digest) {
if (i > 0) {
Entry tmp = bucket->entries[i];
memmove(&bucket->entries[1], &bucket->entries[0], sizeof(Entry) * i);
bucket->entries[0] = tmp;
}
- *file_digest = bucket->entries[0].file_digest;
+ file_digest = bucket->entries[0].file_digest;
if (return_value) {
*return_value = bucket->entries[0].return_value;
}
bool
InodeCache::put(const char* path,
ContentType type,
- const digest& file_digest,
+ const Digest& file_digest,
int return_value)
{
if (!initialize()) {
return false;
}
- digest key_digest;
- if (!hash_inode(path, type, &key_digest)) {
+ Digest key_digest;
+ if (!hash_inode(path, type, key_digest)) {
return false;
}
class Config;
class Context;
-struct digest;
+class Digest;
class InodeCache
{
// otherwise.
bool get(const char* path,
ContentType type,
- digest* file_digest,
+ Digest& file_digest,
int* return_value = nullptr);
// Put hash digest and return value from a successful call to
// Returns true if values could be stored in the cache, false otherwise.
bool put(const char* path,
ContentType type,
- const digest& file_digest,
+ const Digest& file_digest,
int return_value = 0);
// Unmaps the current cache and removes the mapped file from disk.
struct SharedRegion;
bool mmap_file(const std::string& inode_cache_file);
- bool hash_inode(const char* path, ContentType type, digest* digest);
+ bool hash_inode(const char* path, ContentType type, Digest& digest);
Bucket* acquire_bucket(uint32_t index);
- Bucket* acquire_bucket(const digest& key_digest);
+ Bucket* acquire_bucket(const Digest& key_digest);
void release_bucket(Bucket* bucket);
bool create_new_file(const std::string& filename);
bool initialize();
progress_receiver(1.0);
}
+std::string
+format_hex(const uint8_t* data, size_t size)
+{
+ std::string result;
+ result.reserve(size);
+ for (size_t i = 0; i < size; i++) {
+ result += fmt::format("{:02x}", data[i]);
+ }
+ return result;
+}
+
std::string
get_actual_cwd()
{
const SubdirVisitor& visitor,
const ProgressReceiver& progress_receiver);
+// Format a hexadecimal string representing `size` bytes of `data`. The returned
+// string will be `2 * size` long.
+std::string format_hex(const uint8_t* data, size_t size);
+
// Return current working directory (CWD) as returned from getcwd(3) (i.e.,
// normalized path without symlink parts). Returns the empty string on error.
std::string get_actual_cwd();
return false;
}
hash_delimiter(cpp_hash, using_pch_sum ? "pch_sum_hash" : "pch_hash");
- char pch_digest[DIGEST_STRING_BUFFER_SIZE];
- hash_result_as_string(fhash, pch_digest);
- hash_string(cpp_hash, pch_digest);
+ hash_string(cpp_hash, hash_result(fhash).to_string());
}
if (ctx.config.direct_mode()) {
}
}
- digest d;
- hash_result_as_bytes(fhash, &d);
+ Digest d = hash_result(fhash);
ctx.included_files.emplace(path, d);
if (depend_mode_hash) {
hash_delimiter(depend_mode_hash, "include");
- char digest[DIGEST_STRING_BUFFER_SIZE];
- digest_as_string(&d, digest);
- hash_string(depend_mode_hash, digest);
+ hash_string(depend_mode_hash, d.to_string());
}
}
// Extract the used includes from the dependency file. Note that we cannot
// distinguish system headers from other includes here.
-static struct digest*
+static optional<Digest>
result_name_from_depfile(Context& ctx, struct hash* hash)
{
std::string file_content;
cc_log("Cannot open dependency file %s: %s",
ctx.args_info.output_dep.c_str(),
e.what());
- return nullptr;
+ return nullopt;
}
for (string_view token : Util::split_into_views(file_content, " \t\r\n")) {
print_included_files(ctx, stdout);
}
- auto d = static_cast<digest*>(x_malloc(sizeof(digest)));
- hash_result_as_bytes(hash, d);
- return d;
+ return hash_result(hash);
}
// Execute the compiler/preprocessor, with logic to retry without requesting
}
if (ctx.config.depend_mode()) {
- struct digest* result_name =
- result_name_from_depfile(ctx, 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 struct digest*
+static Digest
get_result_name_from_cpp(Context& ctx, Args& args, struct hash* hash)
{
ctx.time_of_compilation = time(nullptr);
hash_string(hash, "false");
}
- auto name = static_cast<digest*>(x_malloc(sizeof(digest)));
- hash_result_as_bytes(hash, name);
- return name;
+ return hash_result(hash);
}
// Hash mtime or content of a file, or the output of a command, according to
// Update a hash sum with information specific to the direct and preprocessor
// modes and calculate the result name. Returns the result name on success,
// otherwise NULL. Caller frees.
-static struct digest*
+static optional<Digest>
calculate_result_name(Context& ctx,
const Args& args,
Args& preprocessor_args,
hash_string(hash, arch);
}
- struct digest* result_name = nullptr;
+ optional<Digest> result_name;
if (direct_mode) {
// Hash environment variables that affect the preprocessor output.
const char* envvars[] = {"CPATH",
if (result & HASH_SOURCE_CODE_FOUND_TIME) {
cc_log("Disabling direct mode");
ctx.config.set_direct_mode(false);
- return nullptr;
+ return nullopt;
}
- struct digest manifest_name;
- hash_result_as_bytes(hash, &manifest_name);
- ctx.set_manifest_name(manifest_name);
+ ctx.set_manifest_name(hash_result(hash));
cc_log("Looking for result name in %s", ctx.manifest_path().c_str());
MTR_BEGIN("manifest", "manifest_get");
cc_log("Got result name from preprocessor with -arch %s",
ctx.args_info.arch_args[i].c_str());
if (i != ctx.args_info.arch_args.size() - 1) {
- free(result_name);
- result_name = nullptr;
+ result_name = nullopt;
}
preprocessor_args.pop_back();
}
args_to_hash.push_back(extra_args_to_hash);
bool put_result_in_manifest = false;
- struct digest* result_name = nullptr;
- struct digest* result_name_from_manifest = nullptr;
+ optional<Digest> result_name;
+ optional<Digest> result_name_from_manifest;
if (ctx.config.direct_mode()) {
cc_log("Trying direct lookup");
MTR_BEGIN("hash", "direct_hash");
}
ctx.set_result_name(*result_name);
- if (result_name_from_manifest
- && !digests_equal(result_name_from_manifest, result_name)) {
+ if (result_name_from_manifest && result_name_from_manifest != result_name) {
// The hash from manifest differs from the hash of the preprocessor
// output. This could be because:
//
} else {
hash_binary_file(ctx, hash, optarg);
}
- char digest[DIGEST_STRING_BUFFER_SIZE];
- hash_result_as_string(hash, digest);
- puts(digest);
+ puts(hash_result(hash).to_string().c_str());
hash_free(hash);
break;
}
#include "hash.hpp"
-#include "legacy_util.hpp"
+#include "Util.hpp"
#include "logging.hpp"
#include <blake2.h>
FILE* debug_text;
};
-void
-digest_as_string(const struct digest* d, char* buffer)
-{
- format_hex(d->bytes, DIGEST_SIZE, buffer);
-}
-
-bool
-digests_equal(const struct digest* d1, const struct digest* d2)
-{
- return memcmp(d1->bytes, d2->bytes, DIGEST_SIZE) == 0;
-}
-
static void
do_hash_buffer(struct hash* hash, const void* s, size_t len)
{
hash_init()
{
auto hash = static_cast<struct hash*>(malloc(sizeof(struct hash)));
- blake2b_init(&hash->state, DIGEST_SIZE);
+ blake2b_init(&hash->state, Digest::size());
hash->debug_binary = nullptr;
hash->debug_text = nullptr;
return hash;
do_debug_text(hash, s, len);
}
-void
-hash_result_as_bytes(struct hash* hash, struct digest* digest)
+Digest
+hash_result(struct hash* hash)
{
// make a copy before altering state
struct hash* copy = hash_copy(hash);
- blake2b_final(©->state, digest->bytes, DIGEST_SIZE);
+ Digest digest;
+ blake2b_final(©->state, digest.bytes(), digest.size());
hash_free(copy);
-}
-
-void
-hash_result_as_string(struct hash* hash, char* buffer)
-{
- struct digest d;
- hash_result_as_bytes(hash, &d);
- digest_as_string(&d, buffer);
+ return digest;
}
void
-// Copyright (C) 2018-2019 Joel Rosdahl and other contributors
+// Copyright (C) 2018-2020 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
#include "system.hpp"
-#include "third_party/nonstd/string_view.hpp"
-
-#define DIGEST_SIZE 20
-#define DIGEST_STRING_BUFFER_SIZE (2 * DIGEST_SIZE + 1)
-
-// struct digest represents the binary form of the final digest (AKA hash or
-// checksum) produced by the hash algorithm.
-struct digest
-{
- uint8_t bytes[DIGEST_SIZE];
-};
+#include "Digest.hpp"
-// Format the digest as a NUL-terminated hex string. The string buffer must
-// contain at least DIGEST_STRING_BUFFER_SIZE bytes.
-void digest_as_string(const struct digest* d, char* buffer);
-
-// Return true if d1 and d2 are equal, else false.
-bool digests_equal(const struct digest* d1, const struct digest* d2);
+#include "third_party/nonstd/string_view.hpp"
// struct hash represents the hash algorithm's inner state.
struct hash;
FILE* debug_binary,
FILE* debug_text);
-// Retrieve the digest as bytes.
-void hash_result_as_bytes(struct hash* hash, struct digest* digest);
-
-// Retrieve the digest as a NUL-terminated hex string. The string buffer must
-// contain at least DIGEST_STRING_BUFFER_SIZE bytes.
-void hash_result_as_string(struct hash* hash, char* buffer);
+// Retrieve the digest.
+Digest hash_result(struct hash* hash);
// Hash some data that is unlikely to occur in the input. The idea is twofold:
//
// files separately so that digests based on file contents can be reused. Then
// add the digest into the outer hash instead.
InodeCache::ContentType content_type = get_content_type(ctx.config, path);
- struct digest digest;
+ Digest digest;
int return_value;
- if (!ctx.inode_cache.get(path, content_type, &digest, &return_value)) {
+ if (!ctx.inode_cache.get(path, content_type, digest, &return_value)) {
struct hash* file_hash = hash_init();
return_value = hash_source_code_file_nocache(
ctx,
if (return_value == HASH_SOURCE_CODE_ERROR) {
return HASH_SOURCE_CODE_ERROR;
}
- hash_result_as_bytes(file_hash, &digest);
+ digest = hash_result(file_hash);
hash_free(file_hash);
ctx.inode_cache.put(path, content_type, digest, return_value);
}
- hash_buffer(hash, &digest.bytes, sizeof(digest::bytes));
+ hash_buffer(hash, digest.bytes(), Digest::size());
return return_value;
#endif
}
// Reusable file hashes must be independent of the outer context. Thus hash
// files separately so that digests based on file contents can be reused. Then
// add the digest into the outer hash instead.
- struct digest digest;
- if (!ctx.inode_cache.get(path, InodeCache::ContentType::binary, &digest)) {
+ Digest digest;
+ if (!ctx.inode_cache.get(path, InodeCache::ContentType::binary, digest)) {
struct hash* file_hash = hash_init();
if (!hash_file(hash, path)) {
return false;
}
- hash_result_as_bytes(file_hash, &digest);
+ digest = hash_result(file_hash);
hash_free(file_hash);
ctx.inode_cache.put(path, InodeCache::ContentType::binary, digest);
}
- hash_buffer(hash, &digest.bytes, sizeof(digest::bytes));
+ hash_buffer(hash, digest.bytes(), Digest::size());
return true;
#else
return hash_file(hash, path);
return ptr;
}
-// Construct a hexadecimal string representing binary data. The buffer must
-// hold at least 2 * size + 1 bytes.
-void
-format_hex(const uint8_t* data, size_t size, char* buffer)
-{
- for (size_t i = 0; i < size; i++) {
- sprintf(&buffer[i * 2], "%02x", (unsigned)data[i]);
- }
- buffer[2 * size] = '\0';
-}
-
// This is like strdup() but dies if the malloc fails.
char*
x_strdup(const char* s)
const char* get_hostname();
const char* tmp_string();
char* format(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
-void format_hex(const uint8_t* data, size_t size, char* buffer);
void reformat(char** ptr, const char* format, ...) ATTR_FORMAT(printf, 2, 3);
char* x_strdup(const char* s);
char* x_strndup(const char* s, size_t n);
#include "Checksum.hpp"
#include "Config.hpp"
#include "Context.hpp"
+#include "Digest.hpp"
#include "File.hpp"
#include "StdMakeUnique.hpp"
#include "ccache.hpp"
// <n_includes> ::= uint32_t
// <include_entry> ::= <path_index> <digest> <fsize> <mtime> <ctime>
// <path_index> ::= uint32_t
-// <digest> ::= DIGEST_SIZE bytes
+// <digest> ::= Digest::size() bytes
// <fsize> ::= uint64_t ; file size
// <mtime> ::= int64_t ; modification time
// <ctime> ::= int64_t ; status change time
// <result> ::= <n_indexes> <include_index>* <name>
// <n_indexes> ::= uint32_t
// <include_index> ::= uint32_t
-// <name> ::= DIGEST_SIZE bytes
+// <name> ::= Digest::size() bytes
// <epilogue> ::= <checksum>
// <checksum> ::= uint64_t ; XXH64 of content bytes
//
// ----------------------------------------------------------------------------
// <n_includes> 4 bytes
// <path_index> 4 bytes
-// <digest> DIGEST_SIZE bytes
+// <digest> Digest::size() bytes
// <fsize> 8 bytes
// <mtime> 8 bytes
// <ctime> 8 bytes
// <n_indexes> 4 bytes
// <include_index> 4 bytes
// ...
-// <name> DIGEST_SIZE bytes
+// <name> Digest::size() bytes
// ...
// checksum 8 bytes
//
// 1: Introduced in ccache 3.0. (Files are always compressed with gzip.)
// 2: Introduced in ccache 4.0.
+using nonstd::nullopt;
+using nonstd::optional;
+
const uint8_t k_manifest_magic[4] = {'c', 'C', 'm', 'F'};
const uint8_t k_manifest_version = 2;
const uint32_t k_max_manifest_entries = 100;
// Index to n_files.
uint32_t index;
// Digest of referenced file.
- struct digest digest;
+ Digest digest;
// Size of referenced file.
uint64_t fsize;
// mtime of referenced file.
bool
operator==(const FileInfo& lhs, const FileInfo& rhs)
{
- return lhs.index == rhs.index && digests_equal(&lhs.digest, &rhs.digest)
+ return lhs.index == rhs.index && lhs.digest == rhs.digest
&& lhs.fsize == rhs.fsize && lhs.mtime == rhs.mtime
&& lhs.ctime == rhs.ctime;
}
std::vector<uint32_t> file_info_indexes;
// Name of the result.
- struct digest name;
+ Digest name;
};
struct ManifestData
void
add_result_entry(
- const struct digest& result_digest,
- const std::unordered_map<std::string, digest>& included_files,
+ const Digest& result_digest,
+ const std::unordered_map<std::string, Digest>& included_files,
time_t time_of_compilation,
bool save_timestamp)
{
uint32_t
get_file_info_index(
const std::string& path,
- const digest& digest,
+ const Digest& digest,
const std::unordered_map<std::string, uint32_t>& mf_files,
const std::unordered_map<FileInfo, uint32_t>& mf_file_infos,
time_t time_of_compilation,
auto& entry = mf->file_infos.back();
reader.read(entry.index);
- reader.read(entry.digest.bytes, DIGEST_SIZE);
+ reader.read(entry.digest.bytes(), Digest::size());
reader.read(entry.fsize);
reader.read(entry.mtime);
reader.read(entry.ctime);
reader.read(file_info_index);
entry.file_info_indexes.push_back(file_info_index);
}
- reader.read(entry.name.bytes, DIGEST_SIZE);
+ reader.read(entry.name.bytes(), Digest::size());
}
reader.finalize();
payload_size += 2 + file.length();
}
payload_size += 4; // n_file_infos
- payload_size += mf.file_infos.size() * (4 + DIGEST_SIZE + 8 + 8 + 8);
+ payload_size += mf.file_infos.size() * (4 + Digest::size() + 8 + 8 + 8);
payload_size += 4; // n_results
for (const auto& result : mf.results) {
payload_size += 4; // n_file_info_indexes
payload_size += result.file_info_indexes.size() * 4;
- payload_size += DIGEST_SIZE;
+ payload_size += Digest::size();
}
AtomicFile atomic_manifest_file(path, AtomicFile::Mode::binary);
writer.write<uint32_t>(mf.file_infos.size());
for (const auto& file_info : mf.file_infos) {
writer.write<uint32_t>(file_info.index);
- writer.write(file_info.digest.bytes, DIGEST_SIZE);
+ writer.write(file_info.digest.bytes(), Digest::size());
writer.write(file_info.fsize);
writer.write(file_info.mtime);
writer.write(file_info.ctime);
for (uint32_t j = 0; j < result.file_info_indexes.size(); ++j) {
writer.write(result.file_info_indexes[j]);
}
- writer.write(result.name.bytes, DIGEST_SIZE);
+ writer.write(result.name.bytes(), Digest::size());
}
writer.finalize();
const ManifestData& mf,
const ResultEntry& result,
std::unordered_map<std::string, FileStats>& stated_files,
- std::unordered_map<std::string, digest>& hashed_files)
+ std::unordered_map<std::string, Digest>& hashed_files)
{
for (uint32_t file_info_index : result.file_info_indexes) {
const auto& fi = mf.file_infos[file_info_index];
return false;
}
- digest actual;
- hash_result_as_bytes(hash, &actual);
+ Digest actual = hash_result(hash);
hash_free(hash);
hashed_files_iter = hashed_files.emplace(path, actual).first;
}
- if (!digests_equal(&fi.digest, &hashed_files_iter->second)) {
+ if (fi.digest != hashed_files_iter->second) {
return false;
}
}
// Try to get the result name from a manifest file. Caller frees. Returns NULL
// on failure.
-struct digest*
+optional<Digest>
manifest_get(const Context& ctx, const std::string& path)
{
std::unique_ptr<ManifestData> mf;
update_mtime(path.c_str());
} else {
cc_log("No such manifest file");
- return nullptr;
+ return nullopt;
}
} catch (const Error& e) {
cc_log("Error: %s", e.what());
- return nullptr;
+ return nullopt;
}
std::unordered_map<std::string, FileStats> stated_files;
- std::unordered_map<std::string, digest> hashed_files;
+ std::unordered_map<std::string, Digest> hashed_files;
// Check newest result first since it's a bit more likely to match.
- struct digest* name = nullptr;
for (uint32_t i = mf->results.size(); i > 0; i--) {
if (verify_result(
ctx, *mf, mf->results[i - 1], stated_files, hashed_files)) {
- name = static_cast<digest*>(x_malloc(sizeof(digest)));
- *name = mf->results[i - 1].name;
- break;
+ return mf->results[i - 1].name;
}
}
- return name;
+ return nullopt;
}
// Put the result name into a manifest file given a set of included files.
bool
manifest_put(const Config& config,
const std::string& path,
- const struct digest& result_name,
- const std::unordered_map<std::string, digest>& included_files,
+ const Digest& result_name,
+ const std::unordered_map<std::string, Digest>& included_files,
time_t time_of_compilation,
bool save_timestamp)
}
fmt::print(stream, "File infos ({}):\n", mf->file_infos.size());
for (unsigned i = 0; i < mf->file_infos.size(); ++i) {
- char digest[DIGEST_STRING_BUFFER_SIZE];
fmt::print(stream, " {}:\n", i);
fmt::print(stream, " Path index: {}\n", mf->file_infos[i].index);
- digest_as_string(&mf->file_infos[i].digest, digest);
- fmt::print(stream, " Hash: {}\n", digest);
+ fmt::print(stream, " Hash: {}\n", mf->file_infos[i].digest.to_string());
fmt::print(stream, " File size: {}\n", mf->file_infos[i].fsize);
fmt::print(stream, " Mtime: {}\n", mf->file_infos[i].mtime);
fmt::print(stream, " Ctime: {}\n", mf->file_infos[i].ctime);
}
fmt::print(stream, "Results ({}):\n", mf->results.size());
for (unsigned i = 0; i < mf->results.size(); ++i) {
- char name[DIGEST_STRING_BUFFER_SIZE];
fmt::print(stream, " {}:\n", i);
fmt::print(stream, " File info indexes:");
for (uint32_t file_info_index : mf->results[i].file_info_indexes) {
fmt::print(stream, " {}", file_info_index);
}
fmt::print(stream, "\n");
- digest_as_string(&mf->results[i].name, name);
- fmt::print(stream, " Name: {}\n", name);
+ fmt::print(stream, " Name: {}\n", mf->results[i].name.to_string());
}
return true;
-// Copyright (C) 2009-2019 Joel Rosdahl and other contributors
+// Copyright (C) 2009-2020 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
#include "system.hpp"
+#include "third_party/nonstd/optional.hpp"
+
#include <string>
#include <unordered_map>
class Config;
class Context;
-struct digest;
+class Digest;
extern const uint8_t k_manifest_magic[4];
extern const uint8_t k_manifest_version;
-struct digest* manifest_get(const Context& ctx, const std::string& path);
+nonstd::optional<Digest> manifest_get(const Context& ctx,
+ const std::string& path);
bool manifest_put(const Config& config,
const std::string& path,
- const struct digest& result_name,
- const std::unordered_map<std::string, digest>& included_files,
+ const Digest& result_name,
+ const std::unordered_map<std::string, Digest>& included_files,
time_t time_of_compilation,
bool save_timestamp);
bool manifest_dump(const std::string& path, FILE* stream);
namespace {
-struct digest
+Digest
digest_from_string(const char* s)
{
- struct digest digest;
+ Digest digest;
struct hash* hash = hash_init();
hash_string(hash, s);
- hash_result_as_bytes(hash, &digest);
+ digest = hash_result(hash);
hash_free(hash);
return digest;
}
bool
-digest_equals_string(const struct digest& digest, const char* s)
+digest_equals_string(const Digest& digest, const char* s)
{
- struct digest rhs = digest_from_string(s);
- return digests_equal(&digest, &rhs);
+ return digest == digest_from_string(s);
}
bool
ctx.config.set_debug(true);
ctx.config.set_inode_cache(false);
- struct digest digest;
+ Digest digest;
int return_value;
CHECK(!ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
+ "a", InodeCache::ContentType::code, digest, &return_value));
CHECK(!ctx.inode_cache.put(
"a", InodeCache::ContentType::code, digest, return_value));
CHECK(ctx.inode_cache.get_hits() == -1);
ctx.inode_cache.drop();
Util::write_file("a", "");
- struct digest digest;
+ Digest digest;
int return_value;
CHECK(!ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
+ "a", InodeCache::ContentType::code, digest, &return_value));
CHECK(ctx.inode_cache.get_hits() == 0);
CHECK(ctx.inode_cache.get_misses() == 1);
CHECK(ctx.inode_cache.get_errors() == 0);
CHECK(put(ctx, "a", "a text", 1));
- struct digest digest;
+ Digest digest;
int return_value;
CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
+ "a", InodeCache::ContentType::code, digest, &return_value));
CHECK(digest_equals_string(digest, "a text"));
CHECK(return_value == 1);
CHECK(ctx.inode_cache.get_hits() == 1);
Util::write_file("a", "something else");
CHECK(!ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
+ "a", InodeCache::ContentType::code, digest, &return_value));
CHECK(ctx.inode_cache.get_hits() == 1);
CHECK(ctx.inode_cache.get_misses() == 1);
CHECK(ctx.inode_cache.get_errors() == 0);
CHECK(put(ctx, "a", "something else", 2));
CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
+ "a", InodeCache::ContentType::code, digest, &return_value));
CHECK(digest_equals_string(digest, "something else"));
CHECK(return_value == 2);
CHECK(ctx.inode_cache.get_hits() == 2);
ctx.config.set_debug(true);
ctx.config.set_inode_cache(true);
- struct digest digest;
+ Digest digest;
- ctx.inode_cache.get("a", InodeCache::ContentType::binary, &digest);
+ ctx.inode_cache.get("a", InodeCache::ContentType::binary, digest);
CHECK(Stat::stat(ctx.inode_cache.get_file()));
CHECK(ctx.inode_cache.drop());
CHECK(!Stat::stat(ctx.inode_cache.get_file()));
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 code_with_sloppy_time_macros_digest =
+ Digest binary_digest = digest_from_string("binary");
+ Digest code_digest = digest_from_string("code");
+ Digest code_with_sloppy_time_macros_digest =
digest_from_string("sloppy_time_macros");
CHECK(ctx.inode_cache.put(
code_with_sloppy_time_macros_digest,
3));
- digest digest;
+ Digest digest;
int return_value;
CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::binary, &digest, &return_value));
- CHECK(digests_equal(&digest, &binary_digest));
+ "a", InodeCache::ContentType::binary, digest, &return_value));
+ CHECK(digest == binary_digest);
CHECK(return_value == 1);
CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, &digest, &return_value));
- CHECK(digests_equal(&digest, &code_digest));
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(digest == code_digest);
CHECK(return_value == 2);
CHECK(
ctx.inode_cache.get("a",
InodeCache::ContentType::code_with_sloppy_time_macros,
- &digest,
+ digest,
&return_value));
- CHECK(digests_equal(&digest, &code_with_sloppy_time_macros_digest));
+ CHECK(digest == code_with_sloppy_time_macros_digest);
CHECK(return_value == 3);
}
CHECK(actual == expected);
}
+TEST_CASE("format_hex")
+{
+ uint8_t none[] = "";
+ uint8_t text[4] = "foo"; // incl. NUL
+ uint8_t data[4] = {0, 1, 2, 3};
+
+ CHECK(Util::format_hex(none, 0) == "");
+ CHECK(Util::format_hex(text, sizeof(text)) == "666f6f00");
+ CHECK(Util::format_hex(data, sizeof(data)) == "00010203");
+}
+
TEST_CASE("Util::get_extension")
{
CHECK(Util::get_extension("") == "");
TEST_CASE("test_known_strings")
{
- char d[DIGEST_STRING_BUFFER_SIZE];
-
{
struct hash* h = hash_init();
hash_string(h, "");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "3345524abf6bbe1809449224b5972c41790b6cf2") == 0);
+ CHECK(hash_result(h).to_string()
+ == "3345524abf6bbe1809449224b5972c41790b6cf2");
hash_free(h);
}
{
struct hash* h = hash_init();
hash_string(h, "a");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "948caa2db61bc4cdb4faf7740cd491f195043914") == 0);
+ CHECK(hash_result(h).to_string()
+ == "948caa2db61bc4cdb4faf7740cd491f195043914");
hash_free(h);
}
{
struct hash* h = hash_init();
hash_string(h, "message digest");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "6bfec6f65e52962be863d6ea1005fc5e4cc8478c") == 0);
+ CHECK(hash_result(h).to_string()
+ == "6bfec6f65e52962be863d6ea1005fc5e4cc8478c");
hash_free(h);
}
"1234567890123456789012345678901234567890123456789012345678901234567890"
"1"
"234567890");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "c2be0e534a67d25947f0c7e78527b2f82abd260f") == 0);
+ CHECK(hash_result(h).to_string()
+ == "c2be0e534a67d25947f0c7e78527b2f82abd260f");
hash_free(h);
}
}
TEST_CASE("hash_result_should_not_alter_state")
{
- char d[DIGEST_STRING_BUFFER_SIZE];
struct hash* h = hash_init();
hash_string(h, "message");
- hash_result_as_string(h, d);
+ hash_result(h);
hash_string(h, " digest");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "6bfec6f65e52962be863d6ea1005fc5e4cc8478c") == 0);
+ CHECK(hash_result(h).to_string()
+ == "6bfec6f65e52962be863d6ea1005fc5e4cc8478c");
hash_free(h);
}
TEST_CASE("hash_result_should_be_idempotent")
{
- char d[DIGEST_STRING_BUFFER_SIZE];
struct hash* h = hash_init();
hash_string(h, "");
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "3345524abf6bbe1809449224b5972c41790b6cf2") == 0);
- hash_result_as_string(h, d);
- CHECK(strcmp(d, "3345524abf6bbe1809449224b5972c41790b6cf2") == 0);
-
+ hash_result(h);
+ CHECK(hash_result(h).to_string()
+ == "3345524abf6bbe1809449224b5972c41790b6cf2");
+ CHECK(hash_result(h).to_string()
+ == "3345524abf6bbe1809449224b5972c41790b6cf2");
hash_free(h);
}
-TEST_CASE("hash_result_as_bytes")
+TEST_CASE("hash_result digest bytes")
{
struct hash* h = hash_init();
hash_string(h, "message digest");
- struct digest d;
- hash_result_as_bytes(h, &d);
- uint8_t expected[sizeof(d.bytes)] = {
+ Digest d = hash_result(h);
+ uint8_t expected[Digest::size()] = {
0x6b, 0xfe, 0xc6, 0xf6, 0x5e, 0x52, 0x96, 0x2b, 0xe8, 0x63,
0xd6, 0xea, 0x10, 0x05, 0xfc, 0x5e, 0x4c, 0xc8, 0x47, 0x8c,
};
- CHECK(memcmp(d.bytes, expected, sizeof(d.bytes)) == 0);
+ CHECK(memcmp(d.bytes(), expected, sizeof(Digest::size())) == 0);
hash_free(h);
}
TEST_CASE("hash_command_output_simple")
{
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_command_output(h1, "echo", "not used"));
CHECK(hash_command_output(h2, "echo", "not used"));
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(strcmp(d1, d2) == 0);
+ CHECK(hash_result(h1) == hash_result(h2));
hash_free(h2);
hash_free(h1);
TEST_CASE("hash_command_output_space_removal")
{
- Context ctx;
-
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_command_output(h1, "echo", "not used"));
CHECK(hash_command_output(h2, " echo ", "not used"));
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(strcmp(d1, d2) == 0);
+ CHECK(hash_result(h1) == hash_result(h2));
hash_free(h2);
hash_free(h1);
TEST_CASE("hash_command_output_hash_inequality")
{
- Context ctx;
-
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_command_output(h1, "echo foo", "not used"));
CHECK(hash_command_output(h2, "echo bar", "not used"));
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(!str_eq(d1, d2));
+ CHECK(hash_result(h1) != hash_result(h2));
hash_free(h2);
hash_free(h1);
TEST_CASE("hash_command_output_compiler_substitution")
{
- Context ctx;
-
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_command_output(h1, "echo foo", "not used"));
CHECK(hash_command_output(h2, "%compiler% foo", "echo"));
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(strcmp(d1, d2) == 0);
+ CHECK(hash_result(h1) == hash_result(h2));
hash_free(h2);
hash_free(h1);
{
TestContext test_context;
- Context ctx;
-
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_command_output(h1, "echo foo", "not used"));
CHECK(hash_command_output(h2, "stderr.bat", "not used"));
#endif
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(strcmp(d1, d2) == 0);
+ CHECK(hash_result(h1) == hash_result(h2));
hash_free(h2);
hash_free(h1);
TEST_CASE("hash_multicommand_output")
{
- Context ctx;
-
- char d1[DIGEST_STRING_BUFFER_SIZE];
- char d2[DIGEST_STRING_BUFFER_SIZE];
-
struct hash* h1 = hash_init();
struct hash* h2 = hash_init();
CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
CHECK(hash_multicommand_output(h1, "foo.bat", "not used"));
#endif
- hash_result_as_string(h1, d1);
- hash_result_as_string(h2, d2);
- CHECK(strcmp(d1, d2) == 0);
+ CHECK(hash_result(h1) == hash_result(h2));
hash_free(h2);
hash_free(h1);
CHECK_STR_EQ_FREE2("foo bar\n", format_command(argv));
}
-
-TEST_CASE("format_hex")
-{
- uint8_t none[] = "";
- uint8_t text[4] = "foo"; // incl. NUL
- uint8_t data[4] = {0, 1, 2, 3};
- char result[2 * sizeof(data) + 1] = ".";
-
- format_hex(none, 0, result);
- CHECK(strcmp("", result) == 0);
-
- format_hex(text, sizeof(text), result);
- CHECK(strcmp("666f6f00", result) == 0);
-
- format_hex(data, sizeof(data), result);
- CHECK(strcmp("00010203", result) == 0);
-}