From f20e75a9eee4dd6fbf9e81130308fbaa77283fa2 Mon Sep 17 00:00:00 2001 From: cat Date: Sun, 15 Feb 2026 04:15:59 +0200 Subject: [PATCH] Added more proper fails and QueryWithAll first iteration (not fully impl) --- headers/Containers/DualkeyMap.hpp | 116 +++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/headers/Containers/DualkeyMap.hpp b/headers/Containers/DualkeyMap.hpp index b96c779..2fcbb35 100644 --- a/headers/Containers/DualkeyMap.hpp +++ b/headers/Containers/DualkeyMap.hpp @@ -11,20 +11,24 @@ #include "../Systems/Logging.hpp" #include "Hashing.hpp" +#include #include +#include #include #include +#include #include #include #include #include +#include #include #include #include namespace Tourmaline::Containers { template + uint64_t baseReservation = 2048> class DualkeyMap { public: using QueryResult = @@ -63,10 +67,10 @@ public: bool isSecondKeyGiven = secondKey.has_value(); if (!isFirstKeyGiven && !isSecondKeyGiven) [[unlikely]] { - Systems::Logging::Log("Failed to Delete! Dualkey maps require at least 1 " - "key to be given, doing nothing.", - "Dualkey Map", Systems::Logging::LogLevel::Warning); - return 0; + Systems::Logging::Log( + "Failed to Delete! DualkeyMap::Delete require at least 1 " + "key to be given! Terminating", + "Dualkey Map", Systems::Logging::LogLevel::Critical); } std::size_t firstKeyHash = @@ -126,10 +130,10 @@ public: bool isSecondKeyGiven = secondKey.has_value(); if (!isFirstKeyGiven && !isSecondKeyGiven) [[unlikely]] { - Systems::Logging::Log("Failed to Query! Dualkey maps require at least 1 " - "key to be given, returning an empty vector.", - "Dualkey Map", Systems::Logging::LogLevel::Warning); - return {}; + Systems::Logging::Log( + "Failed to Query! DualkeyMap::Query require at least 1 " + "key to be given! Terminating", + "Dualkey Map", Systems::Logging::LogLevel::Critical); } std::size_t firstKeyHash = isFirstKeyGiven ? std::hash{}(firstKey.value()) : 0; @@ -175,6 +179,93 @@ public: return finishedQuery; } + template + requires(std::same_as || std::same_as) + [[nodiscard("Discarding a very expensive query!")]] + int QueryWithAll(const Key (&keys)[keyCount]) { + constexpr bool searchingInFirstKey = std::is_same_v; + + // I really can't wait for C++26 contracts + if constexpr (keyCount == 0) { + Systems::Logging::Log("Failed to Query! QueryWithAll require at least 2 " + "key to be given, zero was given! Terminating", + "Dualkey Map", + Systems::Logging::LogLevel::Critical); + } + + // Hoping this never ever gets triggered :sigh: + if constexpr (keyCount == 1) { + Systems::Logging::Log("QueryWithAll should not be used for single key " + "entry! Please use Query for this instead.", + "Dualkey Map", Systems::Logging::LogLevel::Error); + } + + // While we don't necessary need the hashes, + // it just helps us tremendously benefit from short circuit checks + std::array keyHashes; + for (uint64_t index = 0; index < keyCount; index++) { + keyHashes[index] = std::hash{}(keys[index]); + } + + std::vector, keyCount>> + queryResults; + uint64_t hashToCompare; + Key *keyToCompare; + std::conditional_t resultKey; + for (DualkeyHash *hash : hashList) { + // The hell of doing 2 conditions with similar logics in + // the same logical block + if constexpr (searchingInFirstKey) { + hashToCompare = hash->firstKeyHash; + keyToCompare = const_cast(&hash->firstKey); + resultKey = const_cast(&hash->secondKey); + } else { + hashToCompare = hash->secondKeyHash; + keyToCompare = const_cast(&hash->secondKey); + resultKey = const_cast(&hash->firstKey); + } + + // The code above was done to make this code more uniform + for (uint64_t index = 0; index < keyCount; index++) { + if (keyHashes[index] == hashToCompare && keys[index] == *keyToCompare) { + + bool doesExist = false; + for (auto &queryRecord : queryResults) { + if (*queryRecord.resultKey == *resultKey) { + queryRecord.valueQueryResults[index] = &hash->value; + ++queryRecord.howManyFound; + doesExist = true; + break; + } + } + + if (doesExist) { + break; + } + + // Since the result record is not present + // we have to make it + queryResults.emplace_back(); + auto &newRecord = queryResults.back(); + newRecord.resultKey = resultKey; + newRecord.valueQueryResults[index] = &hash->value; + } + } + } + + for (const auto &queryRecord : queryResults) { + Systems::Logging::Log( + std::format("Opposite = {}, found = {}", + reinterpret_cast(queryRecord.resultKey), + queryRecord.howManyFound), + "DKM", Systems::Logging::LogLevel::Info, + queryRecord.howManyFound == keyCount); + } + + return 0; + } + void Scan(std::function scanFunction) { @@ -212,6 +303,13 @@ public: DualkeyMap &operator=(const DualkeyMap &) = delete; private: + template + struct MultiQueryResult { + OppositeKey *resultKey = nullptr; + std::size_t howManyFound = 1; + std::array valueQueryResults; + }; + struct DualkeyHash { DualkeyHash(AKey &&firstKey, BKey &&secondKey, Value &&value) : firstKey(std::move(firstKey)), secondKey(std::move(secondKey)),