From eaad4d1bbd551807d287366fe903b4c2626ffd72 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Mon, 17 Mar 2025 22:54:46 +0100 Subject: [PATCH] Fix Twitch helper caches misbehaving API calls with different sets of arguments could map to the same key value pair, which resulted in unexpected behavior for functions using those caches --- plugins/twitch/twitch-helpers.cpp | 62 +++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/plugins/twitch/twitch-helpers.cpp b/plugins/twitch/twitch-helpers.cpp index 4295b7bf..d41f55b9 100644 --- a/plugins/twitch/twitch-helpers.cpp +++ b/plugins/twitch/twitch-helpers.cpp @@ -29,17 +29,23 @@ public: _headers(headers) { } - bool operator<(const Args &other) const + bool operator==(const Args &other) const { bool ret = true; - ret = ret && _uri < other._uri; - ret = ret && _path < other._path; - ret = ret && _params < other._params; - ret = ret && _data < other._data; - ret = ret && _headers < other._headers; + ret = ret && _uri == other._uri; + ret = ret && _path == other._path; + ret = ret && _params == other._params; + ret = ret && _data == other._data; + ret = ret && _headers == other._headers; return ret; } + const std::string &uri() const { return _uri; } + const std::string &path() const { return _path; } + const std::string &data() const { return _data; } + const httplib::Params ¶ms() const { return _params; } + const httplib::Headers &headers() const { return _headers; } + private: std::string _uri; std::string _path; @@ -48,6 +54,37 @@ private: httplib::Headers _headers; }; +}; // namespace advss + +template<> struct std::hash { + inline std::size_t operator()(const advss::Args &args) const + { + static constexpr auto hash_combine = [](std::size_t &seed, + std::size_t hashValue) { + seed ^= hashValue + 0x9e3779b9 + (seed << 6) + + (seed >> 2); + }; + + std::size_t seed = 0; + hash_combine(seed, std::hash()(args.uri())); + hash_combine(seed, std::hash()(args.path())); + hash_combine(seed, std::hash()(args.data())); + + for (const auto &[key, value] : args.params()) { + hash_combine(seed, std::hash()(key)); + hash_combine(seed, std::hash()(value)); + } + for (const auto &[key, value] : args.headers()) { + hash_combine(seed, std::hash()(key)); + hash_combine(seed, std::hash()(value)); + } + + return seed; + } +}; + +namespace advss { + struct CacheEntry { RequestResult result; std::chrono::system_clock::time_point cacheTime = @@ -61,14 +98,15 @@ static bool cacheIsTooOld(const CacheEntry &cache) return diff >= std::chrono::seconds(cacheTimeoutSeconds); } -static bool cacheIsValid(const std::map &cache, +static bool cacheIsValid(const std::unordered_map &cache, const Args &args) { auto it = cache.find(args); return it != cache.end() && !cacheIsTooOld(it->second); } -static void cleanupCache(std::map &cache, std::mutex &mtx) +static void cleanupCache(std::unordered_map &cache, + std::mutex &mtx) { std::lock_guard lock(mtx); cache.clear(); @@ -181,7 +219,7 @@ RequestResult SendGetRequest(const TwitchToken &token, const std::string &uri, return {}; } - static std::map cache; + static std::unordered_map cache; static std::mutex mtx; [[maybe_unused]] static bool _ = []() { AddPluginCleanupStep([]() { cleanupCache(cache, mtx); }); @@ -251,7 +289,7 @@ RequestResult SendPostRequest(const TwitchToken &token, const std::string &uri, return {}; } - static std::map cache; + static std::unordered_map cache; static std::mutex mtx; [[maybe_unused]] static bool _ = []() { AddPluginCleanupStep([]() { cleanupCache(cache, mtx); }); @@ -322,7 +360,7 @@ RequestResult SendPutRequest(const TwitchToken &token, const std::string &uri, return {}; } - static std::map cache; + static std::unordered_map cache; static std::mutex mtx; [[maybe_unused]] static bool _ = []() { AddPluginCleanupStep([]() { cleanupCache(cache, mtx); }); @@ -393,7 +431,7 @@ RequestResult SendPatchRequest(const TwitchToken &token, const std::string &uri, return {}; } - static std::map cache; + static std::unordered_map cache; static std::mutex mtx; [[maybe_unused]] static bool _ = []() { AddPluginCleanupStep([]() { cleanupCache(cache, mtx); });