From f6b3652ae6c2542dbac948e55d799ec273d5c9bd Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 9 Jul 2022 14:17:57 +1000 Subject: [PATCH] Add MemorySettingsInterface --- src/common/CMakeLists.txt | 3 + src/common/common.vcxproj | 3 + src/common/common.vcxproj.filters | 3 + src/common/heterogeneous_containers.h | 97 ++++++ src/common/memory_settings_interface.cpp | 276 ++++++++++++++++++ .../memory_settings_interface.h} | 18 +- src/duckstation-regtest/CMakeLists.txt | 2 - .../duckstation-regtest.vcxproj | 2 - .../duckstation-regtest.vcxproj.filters | 2 - .../regtest_host_interface.h | 4 +- .../regtest_settings_interface.cpp | 198 ------------- 11 files changed, 395 insertions(+), 213 deletions(-) create mode 100644 src/common/heterogeneous_containers.h create mode 100644 src/common/memory_settings_interface.cpp rename src/{duckstation-regtest/regtest_settings_interface.h => common/memory_settings_interface.h} (83%) delete mode 100644 src/duckstation-regtest/regtest_settings_interface.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a0ff3d5f4..eceb4e251 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(common gl/texture.h hash_combine.h heap_array.h + heterogeneous_containers.h layered_settings_interface.cpp layered_settings_interface.h log.cpp @@ -37,6 +38,8 @@ add_library(common make_array.h md5_digest.cpp md5_digest.h + memory_settings_interface.cpp + memory_settings_interface.h minizip_helpers.cpp minizip_helpers.h path.h diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 8623a9b67..e9e5935ff 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -49,6 +49,7 @@ + @@ -58,6 +59,7 @@ + true @@ -120,6 +122,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 85eb5dc60..22d0cf5a2 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -135,6 +135,8 @@ + + @@ -247,6 +249,7 @@ vulkan + diff --git a/src/common/heterogeneous_containers.h b/src/common/heterogeneous_containers.h new file mode 100644 index 000000000..c6637786d --- /dev/null +++ b/src/common/heterogeneous_containers.h @@ -0,0 +1,97 @@ +/** + * Provides a map template which doesn't require heap allocations for lookups. + */ + +#pragma once + +#include "types.h" +#include +#include +#include +#include +#include + +namespace detail { +struct transparent_string_hash +{ + using is_transparent = void; + + std::size_t operator()(const std::string_view& v) const { return std::hash{}(v); } + std::size_t operator()(const std::string& s) const { return std::hash{}(s); } + std::size_t operator()(const char* s) const { return operator()(std::string_view(s)); } +}; + +struct transparent_string_equal +{ + using is_transparent = void; + + bool operator()(const std::string& lhs, const std::string_view& rhs) const { return lhs == rhs; } + bool operator()(const std::string& lhs, const std::string& rhs) const { return lhs == rhs; } + bool operator()(const std::string& lhs, const char* rhs) const { return lhs == rhs; } + bool operator()(const std::string_view& lhs, const std::string& rhs) const { return lhs == rhs; } + bool operator()(const char* lhs, const std::string& rhs) const { return lhs == rhs; } +}; + +struct transparent_string_less +{ + using is_transparent = void; + + bool operator()(const std::string& lhs, const std::string_view& rhs) const { return lhs < rhs; } + bool operator()(const std::string& lhs, const std::string& rhs) const { return lhs < rhs; } + bool operator()(const std::string& lhs, const char* rhs) const { return lhs < rhs; } + bool operator()(const std::string_view& lhs, const std::string& rhs) const { return lhs < rhs; } + bool operator()(const char* lhs, const std::string& rhs) const { return lhs < rhs; } +}; +} // namespace detail + +// This requires C++20, so fallback to ugly heap allocations if we don't have it. +#if __cplusplus >= 202002L +template +using UnorderedStringMap = + std::unordered_map; +template +using UnorderedStringMultimap = + std::unordered_multimap; +using UnorderedStringSet = + std::unordered_set; +using UnorderedStringMultiSet = + std::unordered_multiset; + +template +ALWAYS_INLINE typename UnorderedStringMap::const_iterator +UnorderedStringMapFind(const UnorderedStringMap& map, const KeyType& key) +{ + return map.find(key); +} +template +ALWAYS_INLINE typename UnorderedStringMap::iterator +UnorderedStringMapFind(UnorderedStringMap& map, const KeyType& key) +{ + return map.find(key); +} +#else +template +using UnorderedStringMap = std::unordered_map; +template +using UnorderedStringMultimap = std::unordered_multimap; +using UnorderedStringSet = std::unordered_set; +using UnorderedStringMultiSet = std::unordered_multiset; + +template +ALWAYS_INLINE typename UnorderedStringMap::const_iterator UnorderedStringMapFind(const UnorderedStringMap& map, const KeyType& key) +{ + return map.find(std::string(key)); +} +template +ALWAYS_INLINE typename UnorderedStringMap::iterator UnorderedStringMapFind(UnorderedStringMap& map, const KeyType& key) +{ + return map.find(std::string(key)); +} +#endif + +template +using StringMap = std::map; +template +using StringMultiMap = std::multimap; +using StringSet = std::set; +using StringMultiSet = std::multiset; diff --git a/src/common/memory_settings_interface.cpp b/src/common/memory_settings_interface.cpp new file mode 100644 index 000000000..586c00bf5 --- /dev/null +++ b/src/common/memory_settings_interface.cpp @@ -0,0 +1,276 @@ +#include "memory_settings_interface.h" +#include "common/assert.h" +#include "common/string_util.h" + +MemorySettingsInterface::MemorySettingsInterface() = default; + +MemorySettingsInterface::~MemorySettingsInterface() = default; + +bool MemorySettingsInterface::Save() +{ + return false; +} + +void MemorySettingsInterface::Clear() +{ + m_sections.clear(); +} + +bool MemorySettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + std::optional parsed = StringUtil::FromChars(iter->second, 10); + if (!parsed.has_value()) + return false; + + *value = parsed.value(); + return true; +} + +bool MemorySettingsInterface::GetUIntValue(const char* section, const char* key, u32* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + std::optional parsed = StringUtil::FromChars(iter->second, 10); + if (!parsed.has_value()) + return false; + + *value = parsed.value(); + return true; +} + +bool MemorySettingsInterface::GetFloatValue(const char* section, const char* key, float* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + std::optional parsed = StringUtil::FromChars(iter->second); + if (!parsed.has_value()) + return false; + + *value = parsed.value(); + return true; +} + +bool MemorySettingsInterface::GetDoubleValue(const char* section, const char* key, double* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + std::optional parsed = StringUtil::FromChars(iter->second); + if (!parsed.has_value()) + return false; + + *value = parsed.value(); + return true; +} + +bool MemorySettingsInterface::GetBoolValue(const char* section, const char* key, bool* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + std::optional parsed = StringUtil::FromChars(iter->second); + if (!parsed.has_value()) + return false; + + *value = parsed.value(); + return true; +} + +bool MemorySettingsInterface::GetStringValue(const char* section, const char* key, std::string* value) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + const auto iter = sit->second.find(key); + if (iter == sit->second.end()) + return false; + + *value = iter->second; + return true; +} + +void MemorySettingsInterface::SetIntValue(const char* section, const char* key, s32 value) +{ + SetValue(section, key, std::to_string(value)); +} + +void MemorySettingsInterface::SetUIntValue(const char* section, const char* key, u32 value) +{ + SetValue(section, key, std::to_string(value)); +} + +void MemorySettingsInterface::SetFloatValue(const char* section, const char* key, float value) +{ + SetValue(section, key, std::to_string(value)); +} + +void MemorySettingsInterface::SetDoubleValue(const char* section, const char* key, double value) +{ + SetValue(section, key, std::to_string(value)); +} + +void MemorySettingsInterface::SetBoolValue(const char* section, const char* key, bool value) +{ + SetValue(section, key, std::to_string(value)); +} + +void MemorySettingsInterface::SetStringValue(const char* section, const char* key, const char* value) +{ + SetValue(section, key, value); +} + +void MemorySettingsInterface::SetValue(const char* section, const char* key, std::string value) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + sit = m_sections.emplace(std::make_pair(std::string(section), KeyMap())).first; + + const auto range = sit->second.equal_range(key); + if (range.first == sit->second.end()) + { + sit->second.emplace(std::string(key), std::move(value)); + return; + } + + auto iter = range.first; + iter->second = std::move(value); + ++iter; + + // remove other values + while (iter != range.second) + { + auto remove = iter++; + sit->second.erase(remove); + } +} + +std::vector MemorySettingsInterface::GetStringList(const char* section, const char* key) const +{ + std::vector ret; + + const auto sit = m_sections.find(section); + if (sit != m_sections.end()) + { + const auto range = sit->second.equal_range(key); + for (auto iter = range.first; iter != range.second; ++iter) + ret.emplace_back(iter->second); + } + + return ret; +} + +void MemorySettingsInterface::SetStringList(const char* section, const char* key, const std::vector& items) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + sit = m_sections.emplace(std::make_pair(std::string(section), KeyMap())).first; + + const auto range = sit->second.equal_range(key); + for (auto iter = range.first; iter != range.second;) + sit->second.erase(iter++); + + std::string_view keysv(key); + for (const std::string& value : items) + sit->second.emplace(keysv, value); +} + +bool MemorySettingsInterface::RemoveFromStringList(const char* section, const char* key, const char* item) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + sit = m_sections.emplace(std::make_pair(std::string(section), KeyMap())).first; + + const auto range = sit->second.equal_range(key); + bool result = false; + for (auto iter = range.first; iter != range.second;) + { + if (iter->second == item) + { + sit->second.erase(iter++); + result = true; + } + else + { + ++iter; + } + } + + return result; +} + +bool MemorySettingsInterface::AddToStringList(const char* section, const char* key, const char* item) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + sit = m_sections.emplace(std::make_pair(std::string(section), KeyMap())).first; + + const auto range = sit->second.equal_range(key); + for (auto iter = range.first; iter != range.second; ++iter) + { + if (iter->second == item) + return false; + } + + sit->second.emplace(std::string(key), std::string(item)); + return true; +} + +bool MemorySettingsInterface::ContainsValue(const char* section, const char* key) const +{ + const auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return false; + + return (sit->second.find(key) != sit->second.end()); +} + +void MemorySettingsInterface::DeleteValue(const char* section, const char* key) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return; + + const auto range = sit->second.equal_range(key); + for (auto iter = range.first; iter != range.second;) + sit->second.erase(iter++); +} + +void MemorySettingsInterface::ClearSection(const char* section) +{ + auto sit = m_sections.find(section); + if (sit == m_sections.end()) + return; + + m_sections.erase(sit); +} diff --git a/src/duckstation-regtest/regtest_settings_interface.h b/src/common/memory_settings_interface.h similarity index 83% rename from src/duckstation-regtest/regtest_settings_interface.h rename to src/common/memory_settings_interface.h index ccbf6930b..787fb794e 100644 --- a/src/duckstation-regtest/regtest_settings_interface.h +++ b/src/common/memory_settings_interface.h @@ -1,13 +1,13 @@ #pragma once -#include "core/settings.h" +#include "heterogeneous_containers.h" +#include "settings_interface.h" #include -#include -class RegTestSettingsInterface final : public SettingsInterface +class MemorySettingsInterface final : public SettingsInterface { public: - RegTestSettingsInterface(); - ~RegTestSettingsInterface(); + MemorySettingsInterface(); + ~MemorySettingsInterface(); bool Save() override; @@ -44,6 +44,10 @@ public: using SettingsInterface::GetUIntValue; private: - using KeyMap = std::unordered_map; - KeyMap m_keys; + using KeyMap = UnorderedStringMultimap; + using SectionMap = UnorderedStringMap; + + void SetValue(const char* section, const char* key, std::string value); + + SectionMap m_sections; }; \ No newline at end of file diff --git a/src/duckstation-regtest/CMakeLists.txt b/src/duckstation-regtest/CMakeLists.txt index 89fbc4969..0cbe8236a 100644 --- a/src/duckstation-regtest/CMakeLists.txt +++ b/src/duckstation-regtest/CMakeLists.txt @@ -3,8 +3,6 @@ add_executable(duckstation-regtest regtest_host_display.h regtest_host_interface.cpp regtest_host_interface.h - regtest_settings_interface.cpp - regtest_settings_interface.h ) target_link_libraries(duckstation-regtest PRIVATE core common frontend-common scmversion) diff --git a/src/duckstation-regtest/duckstation-regtest.vcxproj b/src/duckstation-regtest/duckstation-regtest.vcxproj index 37b1824b0..4752d5266 100644 --- a/src/duckstation-regtest/duckstation-regtest.vcxproj +++ b/src/duckstation-regtest/duckstation-regtest.vcxproj @@ -7,12 +7,10 @@ - - diff --git a/src/duckstation-regtest/duckstation-regtest.vcxproj.filters b/src/duckstation-regtest/duckstation-regtest.vcxproj.filters index 79aad0c97..667d931ba 100644 --- a/src/duckstation-regtest/duckstation-regtest.vcxproj.filters +++ b/src/duckstation-regtest/duckstation-regtest.vcxproj.filters @@ -3,11 +3,9 @@ - - \ No newline at end of file diff --git a/src/duckstation-regtest/regtest_host_interface.h b/src/duckstation-regtest/regtest_host_interface.h index 6631cdc3f..954196356 100644 --- a/src/duckstation-regtest/regtest_host_interface.h +++ b/src/duckstation-regtest/regtest_host_interface.h @@ -1,6 +1,6 @@ #pragma once #include "core/host_interface.h" -#include "regtest_settings_interface.h" +#include "common/memory_settings_interface.h" class RegTestHostInterface final : public HostInterface { @@ -60,6 +60,6 @@ private: void InitializeSettings(); void UpdateSettings(); - RegTestSettingsInterface m_settings_interface; + MemorySettingsInterface m_settings_interface; std::recursive_mutex m_settings_mutex; }; diff --git a/src/duckstation-regtest/regtest_settings_interface.cpp b/src/duckstation-regtest/regtest_settings_interface.cpp deleted file mode 100644 index c7d210d85..000000000 --- a/src/duckstation-regtest/regtest_settings_interface.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "regtest_settings_interface.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/string_util.h" -Log_SetChannel(RegTestSettingsInterface); - -RegTestSettingsInterface::RegTestSettingsInterface() = default; - -RegTestSettingsInterface::~RegTestSettingsInterface() = default; - -bool RegTestSettingsInterface::Save() -{ - return false; -} - -void RegTestSettingsInterface::Clear() -{ - m_keys.clear(); -} - -static std::string GetFullKey(const char* section, const char* key) -{ - return StringUtil::StdStringFromFormat("%s/%s", section, key); -} - -bool RegTestSettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - std::optional parsed = StringUtil::FromChars(iter->second, 10); - if (!parsed.has_value()) - return false; - - *value = parsed.value(); - return true; -} - -bool RegTestSettingsInterface::GetUIntValue(const char* section, const char* key, u32* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - std::optional parsed = StringUtil::FromChars(iter->second, 10); - if (!parsed.has_value()) - return false; - - *value = parsed.value(); - return true; -} - -bool RegTestSettingsInterface::GetFloatValue(const char* section, const char* key, float* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - std::optional parsed = StringUtil::FromChars(iter->second); - if (!parsed.has_value()) - return false; - - *value = parsed.value(); - return true; -} - -bool RegTestSettingsInterface::GetDoubleValue(const char* section, const char* key, double* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - std::optional parsed = StringUtil::FromChars(iter->second); - if (!parsed.has_value()) - return false; - - *value = parsed.value(); - return true; -} - -bool RegTestSettingsInterface::GetBoolValue(const char* section, const char* key, bool* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - std::optional parsed = StringUtil::FromChars(iter->second); - if (!parsed.has_value()) - return false; - - *value = parsed.value(); - return true; -} - -bool RegTestSettingsInterface::GetStringValue(const char* section, const char* key, std::string* value) const -{ - const std::string fullkey(GetFullKey(section, key)); - auto iter = m_keys.find(fullkey); - if (iter == m_keys.end()) - return false; - - *value = iter->second; - return true; -} - -void RegTestSettingsInterface::SetIntValue(const char* section, const char* key, s32 value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = std::to_string(value); -} - -void RegTestSettingsInterface::SetUIntValue(const char* section, const char* key, u32 value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = std::to_string(value); -} - -void RegTestSettingsInterface::SetFloatValue(const char* section, const char* key, float value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = std::to_string(value); -} - -void RegTestSettingsInterface::SetDoubleValue(const char* section, const char* key, double value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = std::to_string(value); -} - -void RegTestSettingsInterface::SetBoolValue(const char* section, const char* key, bool value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = std::string(value ? "true" : "false"); -} - -void RegTestSettingsInterface::SetStringValue(const char* section, const char* key, const char* value) -{ - const std::string fullkey(GetFullKey(section, key)); - m_keys[std::move(fullkey)] = value; -} - -std::vector RegTestSettingsInterface::GetStringList(const char* section, const char* key) const -{ - std::vector ret; - Panic("Not implemented"); - return ret; -} - -void RegTestSettingsInterface::SetStringList(const char* section, const char* key, - const std::vector& items) -{ - Panic("Not implemented"); -} - -bool RegTestSettingsInterface::RemoveFromStringList(const char* section, const char* key, const char* item) -{ - Panic("Not implemented"); - return false; -} - -bool RegTestSettingsInterface::AddToStringList(const char* section, const char* key, const char* item) -{ - Panic("Not implemented"); - return false; -} - -bool RegTestSettingsInterface::ContainsValue(const char* section, const char* key) const -{ - const std::string fullkey(GetFullKey(section, key)); - return (m_keys.find(fullkey) != m_keys.end()); -} - -void RegTestSettingsInterface::DeleteValue(const char* section, const char* key) -{ - const std::string fullkey(GetFullKey(section, key)); - - auto iter = m_keys.find(fullkey); - if (iter != m_keys.end()) - m_keys.erase(iter); -} - -void RegTestSettingsInterface::ClearSection(const char* section) -{ - const std::string start(StringUtil::StdStringFromFormat("%s/", section)); - for (auto iter = m_keys.begin(); iter != m_keys.end();) - { - if (StringUtil::StartsWith(iter->first, start.c_str())) - iter = m_keys.erase(iter); - else - ++iter; - } -}