"Digest is expected to be trivially copyable.");
static_assert(
- static_cast<int>(InodeCache::ContentType::binary) == 0,
+ static_cast<int>(InodeCache::ContentType::raw) == 0,
"Numeric value is part of key, increment version number if changed.");
static_assert(
- static_cast<int>(InodeCache::ContentType::code) == 1,
- "Numeric value is part of key, increment version number if changed.");
-static_assert(
- static_cast<int>(InodeCache::ContentType::code_with_sloppy_time_macros) == 2,
- "Numeric value is part of key, increment version number if changed.");
-static_assert(
- static_cast<int>(InodeCache::ContentType::precompiled_header) == 3,
+ static_cast<int>(InodeCache::ContentType::checked_for_temporal_macros) == 1,
"Numeric value is part of key, increment version number if changed.");
const void* MMAP_FAILED = reinterpret_cast<void*>(-1); // NOLINT: Must cast here
-// Copyright (C) 2020-2021 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
class InodeCache
{
public:
- // Specifies in which role a file was hashed, since the hash result does not
- // only depend on the actual content but also what we used it for. Source code
- // files are scanned for macros while binary files are not as one example.
+ // Specifies in which mode a file was hashed since the hash result does not
+ // only depend on the actual content but also on operations that were
+ // performed that affect the return value. For example, source code files are
+ // normally scanned for macros while binary files are not.
enum class ContentType {
- binary = 0,
- code = 1,
- code_with_sloppy_time_macros = 2,
- precompiled_header = 3,
+ // The file was not scanned for temporal macros.
+ raw = 0,
+ // The file was checked for temporal macros (see check_for_temporal_macros
+ // in hashutil).
+ checked_for_temporal_macros = 1,
};
InodeCache(const Config& config);
~InodeCache();
// Get saved hash digest and return value from a previous call to
- // hash_source_code_file().
+ // do_hash_file() in hashutil.cpp.
//
// Returns true if saved values could be retrieved from the cache, false
// otherwise.
Digest& file_digest,
int* return_value = nullptr);
- // Put hash digest and return value from a successful call to
- // hash_source_code_file().
+ // Put hash digest and return value from a successful call to do_hash_file()
+ // in hashutil.cpp.
//
// Returns true if values could be stored in the cache, false otherwise.
bool put(const std::string& path,
}
// Let's hash the include file content.
- Hash fhash;
+ Digest file_digest;
if (is_pch) {
if (ctx.args_info.included_pch_file.empty()) {
}
}
- if (!hash_binary_file(ctx, fhash, path)) {
+ if (!hash_binary_file(ctx, file_digest, path)) {
return false;
}
cpp_hash.hash_delimiter(using_pch_sum ? "pch_sum_hash" : "pch_hash");
- cpp_hash.hash(fhash.digest().to_string());
+ cpp_hash.hash(file_digest.to_string());
}
if (ctx.config.direct_mode()) {
if (!is_pch) { // else: the file has already been hashed.
- int result = hash_source_code_file(ctx, fhash, path);
+ int result = hash_source_code_file(ctx, file_digest, path);
if (result & HASH_SOURCE_CODE_ERROR
|| result & HASH_SOURCE_CODE_FOUND_TIME) {
return false;
}
}
- Digest d = fhash.digest();
- ctx.included_files.emplace(path, d);
+ ctx.included_files.emplace(path, file_digest);
if (depend_mode_hash) {
depend_mode_hash->hash_delimiter("include");
- depend_mode_hash->hash(d.to_string());
+ depend_mode_hash->hash(file_digest.to_string());
}
}
hash.hash_delimiter("inputfile");
hash.hash(ctx.args_info.input_file);
- hash.hash_delimiter("sourcecode");
- int result = hash_source_code_file(ctx, hash, ctx.args_info.input_file);
+ hash.hash_delimiter("sourcecode hash");
+ Digest input_file_digest;
+ int result =
+ hash_source_code_file(ctx, input_file_digest, ctx.args_info.input_file);
if (result & HASH_SOURCE_CODE_ERROR) {
return nonstd::make_unexpected(Statistic::internal_error);
}
ctx.config.set_direct_mode(false);
return std::make_pair(std::nullopt, std::nullopt);
}
+ hash.hash(input_file_digest.to_string());
manifest_key = hash.digest();
auto hashed_files_iter = hashed_files.find(path);
if (hashed_files_iter == hashed_files.end()) {
- Hash hash;
- int ret = hash_source_code_file(ctx, hash, path, fs.size);
+ Digest actual_digest;
+ int ret = hash_source_code_file(ctx, actual_digest, path, fs.size);
if (ret & HASH_SOURCE_CODE_ERROR) {
LOG("Failed hashing {}", path);
return false;
return false;
}
- Digest actual = hash.digest();
- hashed_files_iter = hashed_files.emplace(path, actual).first;
+ hashed_files_iter = hashed_files.emplace(path, actual_digest).first;
}
if (fi.digest != hashed_files_iter->second) {
#endif
int
-hash_source_code_file_nocache(const Context& ctx,
- Hash& hash,
- const std::string& path,
- size_t size_hint,
- bool is_precompiled)
+do_hash_file(const Context& ctx,
+ Digest& digest,
+ const std::string& path,
+ size_t size_hint,
+ bool check_temporal_macros)
{
- if (is_precompiled) {
- if (hash.hash_file(path)) {
- return HASH_SOURCE_CODE_OK;
- } else {
- return HASH_SOURCE_CODE_ERROR;
- }
- } else {
- std::string data;
- try {
- data = Util::read_file(path, size_hint);
- } catch (core::Error&) {
- return HASH_SOURCE_CODE_ERROR;
+#ifdef INODE_CACHE_SUPPORTED
+ const InodeCache::ContentType content_type =
+ check_temporal_macros ? InodeCache::ContentType::checked_for_temporal_macros
+ : InodeCache::ContentType::raw;
+ if (ctx.config.inode_cache()) {
+ int result;
+ if (ctx.inode_cache.get(path, content_type, digest, &result)) {
+ return result;
}
- int result = hash_source_code_string(ctx, hash, data, path);
- return result;
}
-}
+#else
+ (void)ctx;
+#endif
-#ifdef INODE_CACHE_SUPPORTED
-InodeCache::ContentType
-get_content_type(const Config& config, const std::string& path)
-{
- if (Util::is_precompiled_header(path)) {
- return InodeCache::ContentType::precompiled_header;
+ std::string data;
+ try {
+ data = Util::read_file(path, size_hint);
+ } catch (core::Error&) {
+ return HASH_SOURCE_CODE_ERROR;
}
- if (config.sloppiness().is_enabled(core::Sloppy::time_macros)) {
- return InodeCache::ContentType::code_with_sloppy_time_macros;
+
+ int result = HASH_SOURCE_CODE_OK;
+ if (check_temporal_macros) {
+ result |= check_for_temporal_macros(data);
}
- return InodeCache::ContentType::code;
-}
+
+ Hash hash;
+ hash.hash(data);
+ digest = hash.digest();
+
+#ifdef INODE_CACHE_SUPPORTED
+ ctx.inode_cache.put(path, content_type, digest, result);
#endif
+ return result;
+}
+
} // namespace
int
}
int
-hash_source_code_string(const Context& ctx,
- Hash& hash,
- std::string_view str,
- const std::string& path)
+hash_source_code_file(const Context& ctx,
+ Digest& digest,
+ const std::string& path,
+ size_t size_hint)
{
- int result = HASH_SOURCE_CODE_OK;
+ const bool check_temporal_macros =
+ !ctx.config.sloppiness().is_enabled(core::Sloppy::time_macros);
+ int result =
+ do_hash_file(ctx, digest, path, size_hint, check_temporal_macros);
- // Check for __DATE__, __TIME__ and __TIMESTAMP__if the sloppiness
- // configuration tells us we should.
- if (!(ctx.config.sloppiness().is_enabled(core::Sloppy::time_macros))) {
- result |= check_for_temporal_macros(str);
+ if (!check_temporal_macros || result == HASH_SOURCE_CODE_OK
+ || (result & HASH_SOURCE_CODE_ERROR)) {
+ return result;
}
- // Hash the source string.
- hash.hash(str);
+ if (result & HASH_SOURCE_CODE_FOUND_TIME) {
+ // We don't know for sure that the program actually uses the __TIME__ macro,
+ // but we have to assume it anyway and hash the time stamp. However, that's
+ // not very useful since the chance that we get a cache hit later the same
+ // second should be quite slim... So, just signal back to the caller that
+ // __TIME__ has been found so that the direct mode can be disabled.
+ LOG("Found __TIME__ in {}", path);
+ return result;
+ }
+
+ // __DATE__ or __TIMESTAMP__ found. We now make sure that the digest changes
+ // if the (potential) expansion of those macros changes by computing a new
+ // digest comprising the file digest and time information that represents the
+ // macro expansions.
+
+ Hash hash;
+ hash.hash(digest.to_string());
if (result & HASH_SOURCE_CODE_FOUND_DATE) {
LOG("Found __DATE__ in {}", path);
- // Make sure that the hash sum changes if the (potential) expansion of
- // __DATE__ changes.
hash.hash_delimiter("date");
auto now = Util::localtime();
if (!now) {
hash.hash(source_date_epoch);
}
}
- if (result & HASH_SOURCE_CODE_FOUND_TIME) {
- // We don't know for sure that the program actually uses the __TIME__ macro,
- // but we have to assume it anyway and hash the time stamp. However, that's
- // not very useful since the chance that we get a cache hit later the same
- // second should be quite slim... So, just signal back to the caller that
- // __TIME__ has been found so that the direct mode can be disabled.
- LOG("Found __TIME__ in {}", path);
- }
if (result & HASH_SOURCE_CODE_FOUND_TIMESTAMP) {
LOG("Found __TIMESTAMP__ in {}", path);
- // Make sure that the hash sum changes if the (potential) expansion of
- // __TIMESTAMP__ changes.
const auto stat = Stat::stat(path);
if (!stat) {
return HASH_SOURCE_CODE_ERROR;
hash.hash(timestamp);
}
+ digest = hash.digest();
return result;
}
-int
-hash_source_code_file(const Context& ctx,
- Hash& hash,
- const std::string& path,
- size_t size_hint)
+bool
+hash_binary_file(const Context& ctx,
+ Digest& digest,
+ const std::string& path,
+ size_t size_hint)
{
-#ifdef INODE_CACHE_SUPPORTED
- if (!ctx.config.inode_cache()) {
-#endif
- return hash_source_code_file_nocache(
- ctx, hash, path, size_hint, Util::is_precompiled_header(path));
-
-#ifdef INODE_CACHE_SUPPORTED
- }
-
- // 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.
- InodeCache::ContentType content_type = get_content_type(ctx.config, path);
- Digest digest;
- int return_value;
- if (!ctx.inode_cache.get(path, content_type, digest, &return_value)) {
- Hash file_hash;
- return_value = hash_source_code_file_nocache(
- ctx,
- file_hash,
- path,
- size_hint,
- content_type == InodeCache::ContentType::precompiled_header);
- if (return_value == HASH_SOURCE_CODE_ERROR) {
- return HASH_SOURCE_CODE_ERROR;
- }
- digest = file_hash.digest();
- ctx.inode_cache.put(path, content_type, digest, return_value);
- }
- hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary);
- return return_value;
-#endif
+ return do_hash_file(ctx, digest, path, size_hint, false)
+ == HASH_SOURCE_CODE_OK;
}
bool
hash_binary_file(const Context& ctx, Hash& hash, const std::string& path)
{
- if (!ctx.config.inode_cache()) {
- return hash.hash_file(path);
- }
-
-#ifdef INODE_CACHE_SUPPORTED
- // 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.
Digest digest;
- if (!ctx.inode_cache.get(path, InodeCache::ContentType::binary, digest)) {
- Hash file_hash;
- if (!file_hash.hash_file(path)) {
- return false;
- }
- digest = file_hash.digest();
- ctx.inode_cache.put(path, InodeCache::ContentType::binary, digest);
+ const bool success = hash_binary_file(ctx, digest, path);
+ if (success) {
+ hash.hash(digest.to_string());
}
- hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary);
- return true;
-#else
- return hash.hash_file(path);
-#endif
+ return success;
}
bool
-// Copyright (C) 2009-2021 Joel Rosdahl and other contributors
+// Copyright (C) 2009-2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
class Config;
class Context;
+class Digest;
class Hash;
const int HASH_SOURCE_CODE_OK = 0;
// appropriately.
int check_for_temporal_macros(std::string_view str);
-// Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results.
-int hash_source_code_string(const Context& ctx,
- Hash& hash,
- std::string_view str,
- const std::string& path);
-
-// Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
-// results.
+// Hash a source code file using the inode cache if enabled. Returns a bitmask
+// of HASH_SOURCE_CODE_* results.
int hash_source_code_file(const Context& ctx,
- Hash& hash,
+ Digest& digest,
const std::string& path,
size_t size_hint = 0);
-// Hash a binary file using the inode cache if enabled.
+// Hash a binary file (using the inode cache if enabled) and put its digest in
+// `digest`
+//
+// Returns true on success, otherwise false.
+bool hash_binary_file(const Context& ctx,
+ Digest& digest,
+ const std::string& path,
+ size_t size_hint = 0);
+
+// Hash a binary file (using the inode cache if enabled) and hash the digest to
+// `hash`.
//
// Returns true on success, otherwise false.
bool hash_binary_file(const Context& ctx, Hash& hash, const std::string& path);
-// Copyright (C) 2020 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2022 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
const std::string& str,
int return_value)
{
- return ctx.inode_cache.put(filename,
- InodeCache::ContentType::code,
- Hash().hash(str).digest(),
- return_value);
+ return ctx.inode_cache.put(
+ filename,
+ InodeCache::ContentType::checked_for_temporal_macros,
+ Hash().hash(str).digest(),
+ return_value);
}
} // namespace
Digest digest;
int return_value;
- CHECK(!ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, digest, &return_value));
- CHECK(!ctx.inode_cache.put(
- "a", InodeCache::ContentType::code, digest, return_value));
+ CHECK(
+ !ctx.inode_cache.get("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ digest,
+ &return_value));
+ CHECK(
+ !ctx.inode_cache.put("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ digest,
+ return_value));
CHECK(ctx.inode_cache.get_hits() == -1);
CHECK(ctx.inode_cache.get_misses() == -1);
CHECK(ctx.inode_cache.get_errors() == -1);
Digest digest;
int return_value;
- CHECK(!ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(
+ !ctx.inode_cache.get("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ digest,
+ &return_value));
CHECK(ctx.inode_cache.get_hits() == 0);
CHECK(ctx.inode_cache.get_misses() == 1);
CHECK(ctx.inode_cache.get_errors() == 0);
Digest digest;
int return_value;
- CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(
+ ctx.inode_cache.get("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ digest,
+ &return_value));
CHECK(digest == Hash().hash("a text").digest());
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));
+ CHECK(
+ !ctx.inode_cache.get("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ 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));
+ CHECK(
+ ctx.inode_cache.get("a",
+ InodeCache::ContentType::checked_for_temporal_macros,
+ digest,
+ &return_value));
CHECK(digest == Hash().hash("something else").digest());
CHECK(return_value == 2);
CHECK(ctx.inode_cache.get_hits() == 2);
Digest digest;
- ctx.inode_cache.get("a", InodeCache::ContentType::binary, digest);
+ ctx.inode_cache.get("a", InodeCache::ContentType::raw, digest);
CHECK(Stat::stat(ctx.inode_cache.get_file()));
CHECK(ctx.inode_cache.drop());
CHECK(!Stat::stat(ctx.inode_cache.get_file()));
Util::write_file("a", "a text");
Digest binary_digest = Hash().hash("binary").digest();
Digest code_digest = Hash().hash("code").digest();
- Digest code_with_sloppy_time_macros_digest =
- Hash().hash("sloppy_time_macros").digest();
- CHECK(ctx.inode_cache.put(
- "a", InodeCache::ContentType::binary, binary_digest, 1));
CHECK(
- ctx.inode_cache.put("a", InodeCache::ContentType::code, code_digest, 2));
- CHECK(
- ctx.inode_cache.put("a",
- InodeCache::ContentType::code_with_sloppy_time_macros,
- code_with_sloppy_time_macros_digest,
- 3));
+ ctx.inode_cache.put("a", InodeCache::ContentType::raw, binary_digest, 1));
+ CHECK(ctx.inode_cache.put(
+ "a", InodeCache::ContentType::checked_for_temporal_macros, code_digest, 2));
Digest digest;
int return_value;
CHECK(ctx.inode_cache.get(
- "a", InodeCache::ContentType::binary, digest, &return_value));
+ "a", InodeCache::ContentType::raw, digest, &return_value));
CHECK(digest == binary_digest);
CHECK(return_value == 1);
- CHECK(ctx.inode_cache.get(
- "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,
+ InodeCache::ContentType::checked_for_temporal_macros,
digest,
&return_value));
- CHECK(digest == code_with_sloppy_time_macros_digest);
- CHECK(return_value == 3);
+ CHECK(digest == code_digest);
+ CHECK(return_value == 2);
}
TEST_SUITE_END();