Skip to content

Commit

Permalink
cachemgr: compare cache mappings method to find differences
Browse files Browse the repository at this point in the history
between the configuration file and actual directories on the filesystem
  • Loading branch information
magiruuvelvet committed Oct 2, 2023
1 parent 2f2e419 commit 1f91d62
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/libcachemgr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# create library
add_library(libcachemgr-private STATIC
cache_mapping.hpp
cachemgr.cpp
cachemgr.hpp
config.cpp
Expand Down
23 changes: 23 additions & 0 deletions src/libcachemgr/cache_mapping.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <string>
#include <list>

namespace libcachemgr {

/**
* Represents a cache mapping in the configuration file.
*/
struct cache_mapping_t
{
const std::string type; // unused for now, type might change in the future
const std::string source;
const std::string target;
};

/**
* List of cache mappings in the configuration file.
*/
using cache_mappings_t = std::list<cache_mapping_t>;

} // namespace libcachemgr
109 changes: 108 additions & 1 deletion src/libcachemgr/cachemgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "logging.hpp"

#include <filesystem>
#include <unordered_map>
#include <string_view>

#include <utils/os_utils.hpp>

Expand All @@ -17,8 +19,14 @@ cachemgr_t::~cachemgr_t()

bool cachemgr_t::find_symlinked_cache_directories(const std::string &path, const std::string &symlink_target_prefix) noexcept
{
// ensure the {_clear_previous_list} boolean is always set to true after this function returns
after_run_callback arc(&this->_clear_previous_list);

// clear previous results
this->_symlinked_cache_directories.clear();
if (this->_clear_previous_list)
{
this->_symlinked_cache_directories.clear();
}

// input path must be a directory
if (!fs::is_directory(path, this->_last_system_error))
Expand Down Expand Up @@ -81,3 +89,102 @@ bool cachemgr_t::find_symlinked_cache_directories(const std::string &path, const

return true;
}

bool cachemgr_t::find_symlinked_cache_directories(
const std::initializer_list<std::string> &paths, const std::string &symlink_target_prefix) noexcept
{
// clear previous results
this->_symlinked_cache_directories.clear();

// iterate over all given paths and search for symlinked directories
for (const auto &path : paths)
{
// don't clear the results from the previous search
this->_clear_previous_list = false;

// search the next path for symlinked directories
if (!this->find_symlinked_cache_directories(path, symlink_target_prefix))
{
// abort if any of the searches failed
return false;
}
}

return true;
}

cachemgr_t::cache_mappings_compare_results_t cachemgr_t::compare_cache_mappings(
const libcachemgr::cache_mappings_t &cache_mappings) const noexcept
{
using cache_mappings_compare_result_t = cache_mappings_compare_results_t::cache_mappings_compare_result_t;
cache_mappings_compare_results_t compare_results;

// allow for fast lookup
std::unordered_map<std::string_view, std::list<symlinked_cache_directory_t>::const_iterator> unordered_cache_directories;
for (auto it = this->_symlinked_cache_directories.begin(); it != this->_symlinked_cache_directories.end(); ++it)
{
unordered_cache_directories[it->original_path] = it;
}

// the given cache mapping list takes precedence,
// avoid comparing entries which are not interesting to the user (according to the configuration file)
for (const auto &cache_mapping : cache_mappings)
{
// find the corresponding entry in the unordered_cache_directories map
const auto cache_directory_it = unordered_cache_directories.find(cache_mapping.source);

/**
* Lambda function to push the difference to the result list.
*
* @param cache_directory actual cache directory
*/
const auto push_compare_result = [&compare_results, &cache_mapping](const symlinked_cache_directory_t &cache_directory) {
// push difference to the result list
compare_results.add_result(cache_mappings_compare_result_t{
// cache directory is actually this
.actual = libcachemgr::cache_mapping_t{
.type = cache_mapping.type,
.source = cache_directory.original_path,
.target = cache_directory.target_path,
},
// cache directory expected to be this
.expected = libcachemgr::cache_mapping_t{
.type = cache_mapping.type,
.source = cache_mapping.source,
.target = cache_mapping.target,
},
});
};

// cache directory found
if (cache_directory_it != unordered_cache_directories.end())
{
// get reference to the cache directory entry
const auto &cache_directory = *cache_directory_it->second;

LOG_DEBUG(libcachemgr::log_cachemgr,
"cachemgr_t::compare_cache_mappings(): cache_directory.target_path='{}', cache_mapping.target='{}'",
cache_directory.target_path, cache_mapping.target);

// compare if the target paths are equal
if (cache_directory.target_path != cache_mapping.target)
{
// push difference to the result list
push_compare_result(cache_directory);
}
}

// cache directory not found
else
{
LOG_DEBUG(libcachemgr::log_cachemgr,
"cachemgr_t::compare_cache_mappings(): cache_directory.target_path=[NOT FOUND], cache_mapping.target='{}'",
cache_mapping.target);

// push difference to the result list
push_compare_result(symlinked_cache_directory_t{});
}
}

return compare_results;
}
110 changes: 110 additions & 0 deletions src/libcachemgr/cachemgr.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#pragma once

#include "cache_mapping.hpp"

#include <string>
#include <list>
#include <initializer_list>
#include <system_error>

/**
Expand All @@ -10,6 +13,68 @@
class cachemgr_t final
{
public:
/**
* Data structure to store comparison results for cache mappings.
*/
struct cache_mappings_compare_results_t
{
public:
/**
* Standalone read-only copies of {libcachemgr::cache_mapping_t} for comparison.
*/
struct cache_mappings_compare_result_t
{
/// the actual cache mapping, as found by the cache manager.
const libcachemgr::cache_mapping_t actual;
/// the expected cache mapping, as defined by the configuration file.
const libcachemgr::cache_mapping_t expected;
};

/**
* Push a new comparison result to the internal list.
*
* @param result actual and expected {libcachemgr::cache_mapping_t} object
*/
constexpr inline void add_result(const cache_mappings_compare_result_t &result)
{
this->_differences.emplace_back(result);
}

/**
* Implicit boolean conversion operator to check if the comparison result has differences.
*
* @return true the internal result list contains differences
* @return false the internal result list is empty
*/
constexpr inline operator bool() const
{
// return true if the internal result list contains differences
return this->_differences.size() > 0;
}

/**
* Get the count of differences.
*/
constexpr inline auto count() const
{
return this->_differences.size();
}

/**
* Get the list of differences.
*/
constexpr inline const auto &differences() const
{
return this->_differences;
}

private:
/**
* List of {cache_mappings_compare_result_t}.
*/
std::list<const cache_mappings_compare_result_t> _differences;
};

/**
* Constructs and initializes a new cache manager.
*
Expand Down Expand Up @@ -69,6 +134,31 @@ class cachemgr_t final
*/
bool find_symlinked_cache_directories(const std::string &path, const std::string &symlink_target_prefix = {}) noexcept;

/**
* Searches the given directories for any symlinked cache directories.
*
* Only symbolic links which point to directories starting with the {symlink_target_prefix} are used.
*
* The results are stored in {_symlinked_cache_directories}.
*
* @param paths directories in which symlinked cache directories should be searched
* @param symlink_target_prefix the prefix of the symlink target, empty to include all symlink targets
* @return true search was successful
* @return false error accessing directory, see {get_last_system_error} for an explanation
*/
bool find_symlinked_cache_directories(
const std::initializer_list<std::string> &paths, const std::string &symlink_target_prefix = {}) noexcept;

/**
* Compares the given cache mapping list (from the configuration file) with the found symlinked cache directories.
*
* The results are stored in comparison results structure.
*
* @param cache_mappings the cache mapping list from the configuration file
* @return comparison results
*/
cache_mappings_compare_results_t compare_cache_mappings(const libcachemgr::cache_mappings_t &cache_mappings) const noexcept;

/**
* Returns the symlinked cache directories.
*/
Expand All @@ -95,4 +185,24 @@ class cachemgr_t final
* Last system error encountered by the cache manager.
*/
std::error_code _last_system_error;

/**
* Helper boolean to determine if the previous {_symlinked_cache_directories} should be cleared
* before rescanning the given directories.
*/
bool _clear_previous_list = true;

/// callback to execute after the return statement
struct after_run_callback final
{
after_run_callback(bool *clear_previous_list)
: clear_previous_list(clear_previous_list) {}
~after_run_callback()
{
// reset back to default value
*this->clear_previous_list = true;
}

bool *clear_previous_list = nullptr;
};
};
19 changes: 5 additions & 14 deletions src/libcachemgr/config.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "cache_mapping.hpp"

#include <string>
#include <list>

Expand All @@ -9,20 +11,9 @@
class configuration_t final
{
public:
/**
* Represents a cache mapping in the configuration file.
*/
struct cache_mapping_t
{
const std::string type; // unused for now, type might change in the future
const std::string source;
const std::string target;
};

/**
* List of cache mappings in the configuration file.
*/
using cache_mappings_t = std::list<cache_mapping_t>;
// forward declarations
using cache_mapping_t = libcachemgr::cache_mapping_t;
using cache_mappings_t = libcachemgr::cache_mappings_t;

/**
* Possible error codes related to file handling.
Expand Down

0 comments on commit 1f91d62

Please sign in to comment.