Skip to content

Commit

Permalink
Improve caching
Browse files Browse the repository at this point in the history
Rely on modified time
Strip extra code
  • Loading branch information
AlexMacocian committed May 8, 2024
1 parent cc5f713 commit bebb5e8
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 93 deletions.
1 change: 1 addition & 0 deletions GWToolboxdll/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ target_link_libraries(GWToolboxdll PRIVATE
IconFontCppHeaders
nativefiledialog
wintoast
wolfssl

# libs:
Dbghelp.lib # for MiniDump
Expand Down
111 changes: 27 additions & 84 deletions GWToolboxdll/Modules/Resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,95 +543,44 @@ void Resources::Download(const std::string& url, AsyncLoadMbCallback callback, v

void Resources::Download(const std::string& url, AsyncLoadMbCallback callback, void* context, std::chrono::seconds cache_duration)
{
const auto get_cache_expiration = [](const std::filesystem::path& file_name) -> std::optional<std::chrono::seconds> {
/*
* Load the expiration time from the cache. The expiration time
* is the last 8 bytes at the end of the file. They represent
* the amount of seconds from epoch until when the cache should expire.
*/
std::ifstream cache_file(file_name, std::ios::binary | std::ios::ate);
if (!cache_file.is_open()) {
return std::optional<std::chrono::seconds>();
}
const auto hash_name = [](const std::filesystem::path& file_name) -> std::filesystem::path {
const auto str = file_name.string();
const auto bytes_to_hash = std::vector<byte>(str.begin(), str.end());
auto hash = std::vector<byte>(WC_MD5_DIGEST_SIZE);
wc_Md5Hash(bytes_to_hash.data(), bytes_to_hash.size(), hash.data());
const auto hash_str = std::string(hash.begin(), hash.end());
const auto hash_file = std::filesystem::path(hash_str);
return hash_file;
};

try {
std::streampos file_size = cache_file.tellg();
if (file_size < 8) {
cache_file.close();
return std::optional<std::chrono::seconds>();
}
cache_file.seekg(-8, std::ios::end);
std::vector<char> duration_bytes;
duration_bytes.resize(8);

cache_file.read(duration_bytes.data(), 8);
long long seconds;
std::memcpy(&seconds, duration_bytes.data(), sizeof(seconds));
cache_file.close();
return std::chrono::seconds(seconds);
}
catch (...) {
cache_file.close();
return std::optional<std::chrono::seconds>();
const auto get_cache_modified_time = [&hash_name](const std::filesystem::path& file_name) -> std::optional<std::filesystem::file_time_type> {
if (!std::filesystem::exists(file_name)) {
return std::optional<std::filesystem::file_time_type>();
}

const auto file_time = std::filesystem::last_write_time(file_name);
return file_time;
};

const auto load_from_cache = [](const std::filesystem::path& file_name) -> std::optional<std::string> {
/*
* Load the file from cache. The last 8 bytes
* are the expiration time, so we don't need to load those.
*/
std::ifstream cache_file(file_name, std::ios::binary | std::ios::ate);
std::ifstream cache_file(file_name);
if (!cache_file.is_open()) {
return std::optional<std::string>();
}

try {
std::streampos file_size = cache_file.tellg();
if (file_size < 8) {
cache_file.close();
return std::optional<std::string>();
}

cache_file.seekg(0, std::ios::beg);
std::string file_buffer;
file_buffer.resize(static_cast<size_t>(file_size - std::streamoff(8)));
cache_file.read(file_buffer.data(), file_buffer.size());
cache_file.close();
return file_buffer;
} catch (...) {
cache_file.close();
return std::optional<std::string>();
}
std::string contents((std::istreambuf_iterator<char>(cache_file)), std::istreambuf_iterator<char>());
return contents;
};

const auto save_to_cache = [](const std::filesystem::path& file_name, const std::string& content, const std::chrono::seconds cache_duration) -> bool {
/*
* Save the file contents to a cached file. At the end of the file,
* append 8 bytes representing the amount of seconds since epoch
* until the cache should expire
*/
const auto save_to_cache = [](const std::filesystem::path& file_name, const std::string& content) -> bool {
std::filesystem::create_directories(file_name.parent_path());
std::ofstream cache_file(file_name);
if (!cache_file.is_open()) {
return false;
}

try {
cache_file << content;
std::vector<char> duration_buff;
const long long seconds = cache_duration.count();
duration_buff.resize(8);
std::memcpy(duration_buff.data(), &seconds, duration_buff.size());
for (const auto c : duration_buff) {
cache_file << c;
}
cache_file.close();
return true;
} catch (...) {
cache_file.close();
return false;
}
cache_file << content;
return true;
};

const auto remove_protocol = [](const std::string& url) -> std::string {
Expand All @@ -648,18 +597,13 @@ void Resources::Download(const std::string& url, AsyncLoadMbCallback callback, v
return url; // Return the original if no match is found
};

EnqueueWorkerTask([url, callback, context, cache_duration, &get_cache_expiration, &load_from_cache, &save_to_cache, &remove_protocol] {
EnqueueWorkerTask([url, callback, context, cache_duration, &get_cache_modified_time, &load_from_cache, &save_to_cache, &remove_protocol, &hash_name] {
const auto computer_path = Resources::GetComputerFolderPath();
auto cache_path_str = (computer_path / "cache" / remove_protocol(url)).string();
for (char& c : cache_path_str) {
if (c == '/') {
c = '\\';
}
}
const auto cache_path = std::filesystem::path(cache_path_str);
const auto expiration = get_cache_expiration(cache_path);
const auto cache_path = (computer_path / "cache" / remove_protocol(url));
const auto hashed_path = hash_name(cache_path);
const auto expiration = get_cache_modified_time(cache_path);
if (expiration.has_value() &&
expiration.value() > std::chrono::system_clock::now().time_since_epoch()) {
expiration.value() - std::chrono::file_clock::now() < std::chrono::days(30)) {
const auto response = load_from_cache(cache_path);
if (response.has_value()) {
EnqueueMainTask([callback, context, response] {
Expand All @@ -670,8 +614,7 @@ void Resources::Download(const std::string& url, AsyncLoadMbCallback callback, v
}
std::string response;
bool ok = Download(url, response);
const auto expiration_time = std::chrono::duration_cast<std::chrono::seconds>((std::chrono::system_clock::now() + cache_duration).time_since_epoch());
save_to_cache(cache_path, response, expiration_time);
save_to_cache(cache_path, response);
EnqueueMainTask([callback, ok, response, context] {
callback(ok, response, context);
});
Expand Down
9 changes: 0 additions & 9 deletions cmake/openssl.cmake

This file was deleted.

0 comments on commit bebb5e8

Please sign in to comment.