Add MemorySettingsInterface

This commit is contained in:
Connor McLaughlin 2022-07-09 14:17:57 +10:00
parent 462eb2c155
commit f6b3652ae6
11 changed files with 395 additions and 213 deletions

View File

@ -30,6 +30,7 @@ add_library(common
gl/texture.h gl/texture.h
hash_combine.h hash_combine.h
heap_array.h heap_array.h
heterogeneous_containers.h
layered_settings_interface.cpp layered_settings_interface.cpp
layered_settings_interface.h layered_settings_interface.h
log.cpp log.cpp
@ -37,6 +38,8 @@ add_library(common
make_array.h make_array.h
md5_digest.cpp md5_digest.cpp
md5_digest.h md5_digest.h
memory_settings_interface.cpp
memory_settings_interface.h
minizip_helpers.cpp minizip_helpers.cpp
minizip_helpers.h minizip_helpers.h
path.h path.h

View File

@ -49,6 +49,7 @@
<ClInclude Include="log.h" /> <ClInclude Include="log.h" />
<ClInclude Include="lru_cache.h" /> <ClInclude Include="lru_cache.h" />
<ClInclude Include="make_array.h" /> <ClInclude Include="make_array.h" />
<ClInclude Include="memory_settings_interface.h" />
<ClInclude Include="md5_digest.h" /> <ClInclude Include="md5_digest.h" />
<ClInclude Include="path.h" /> <ClInclude Include="path.h" />
<ClInclude Include="pbp_types.h" /> <ClInclude Include="pbp_types.h" />
@ -58,6 +59,7 @@
<ClInclude Include="scope_guard.h" /> <ClInclude Include="scope_guard.h" />
<ClInclude Include="settings_interface.h" /> <ClInclude Include="settings_interface.h" />
<ClInclude Include="string.h" /> <ClInclude Include="string.h" />
<ClInclude Include="heterogeneous_containers.h" />
<ClInclude Include="string_util.h" /> <ClInclude Include="string_util.h" />
<ClInclude Include="thirdparty\StackWalker.h"> <ClInclude Include="thirdparty\StackWalker.h">
<ExcludedFromBuild Condition="'$(BuildingForUWP)'=='true'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(BuildingForUWP)'=='true'">true</ExcludedFromBuild>
@ -120,6 +122,7 @@
<ClCompile Include="image.cpp" /> <ClCompile Include="image.cpp" />
<ClCompile Include="layered_settings_interface.cpp" /> <ClCompile Include="layered_settings_interface.cpp" />
<ClCompile Include="log.cpp" /> <ClCompile Include="log.cpp" />
<ClCompile Include="memory_settings_interface.cpp" />
<ClCompile Include="md5_digest.cpp" /> <ClCompile Include="md5_digest.cpp" />
<ClCompile Include="minizip_helpers.cpp" /> <ClCompile Include="minizip_helpers.cpp" />
<ClCompile Include="progress_callback.cpp" /> <ClCompile Include="progress_callback.cpp" />

View File

@ -135,6 +135,8 @@
</ClInclude> </ClInclude>
<ClInclude Include="settings_interface.h" /> <ClInclude Include="settings_interface.h" />
<ClInclude Include="layered_settings_interface.h" /> <ClInclude Include="layered_settings_interface.h" />
<ClInclude Include="heterogeneous_containers.h" />
<ClInclude Include="memory_settings_interface.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="gl\program.cpp"> <ClCompile Include="gl\program.cpp">
@ -247,6 +249,7 @@
<Filter>vulkan</Filter> <Filter>vulkan</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layered_settings_interface.cpp" /> <ClCompile Include="layered_settings_interface.cpp" />
<ClCompile Include="memory_settings_interface.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Natvis Include="bitfield.natvis" /> <Natvis Include="bitfield.natvis" />

View File

@ -0,0 +1,97 @@
/**
* Provides a map template which doesn't require heap allocations for lookups.
*/
#pragma once
#include "types.h"
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace detail {
struct transparent_string_hash
{
using is_transparent = void;
std::size_t operator()(const std::string_view& v) const { return std::hash<std::string_view>{}(v); }
std::size_t operator()(const std::string& s) const { return std::hash<std::string>{}(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<typename ValueType>
using UnorderedStringMap =
std::unordered_map<std::string, ValueType, detail::transparent_string_hash, detail::transparent_string_equal>;
template<typename ValueType>
using UnorderedStringMultimap =
std::unordered_multimap<std::string, ValueType, detail::transparent_string_hash, detail::transparent_string_equal>;
using UnorderedStringSet =
std::unordered_set<std::string, detail::transparent_string_hash, detail::transparent_string_equal>;
using UnorderedStringMultiSet =
std::unordered_multiset<std::string, detail::transparent_string_hash, detail::transparent_string_equal>;
template<typename KeyType, typename ValueType>
ALWAYS_INLINE typename UnorderedStringMap<ValueType>::const_iterator
UnorderedStringMapFind(const UnorderedStringMap<ValueType>& map, const KeyType& key)
{
return map.find(key);
}
template<typename KeyType, typename ValueType>
ALWAYS_INLINE typename UnorderedStringMap<ValueType>::iterator
UnorderedStringMapFind(UnorderedStringMap<ValueType>& map, const KeyType& key)
{
return map.find(key);
}
#else
template<typename ValueType>
using UnorderedStringMap = std::unordered_map<std::string, ValueType>;
template<typename ValueType>
using UnorderedStringMultimap = std::unordered_multimap<std::string, ValueType>;
using UnorderedStringSet = std::unordered_set<std::string>;
using UnorderedStringMultiSet = std::unordered_multiset<std::string>;
template<typename KeyType, typename ValueType>
ALWAYS_INLINE typename UnorderedStringMap<ValueType>::const_iterator UnorderedStringMapFind(const UnorderedStringMap<ValueType>& map, const KeyType& key)
{
return map.find(std::string(key));
}
template<typename KeyType, typename ValueType>
ALWAYS_INLINE typename UnorderedStringMap<ValueType>::iterator UnorderedStringMapFind(UnorderedStringMap<ValueType>& map, const KeyType& key)
{
return map.find(std::string(key));
}
#endif
template<typename ValueType>
using StringMap = std::map<std::string, ValueType, detail::transparent_string_less>;
template<typename ValueType>
using StringMultiMap = std::multimap<std::string, ValueType, detail::transparent_string_less>;
using StringSet = std::set<std::string, detail::transparent_string_less>;
using StringMultiSet = std::multiset<std::string, detail::transparent_string_less>;

View File

@ -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<s32> parsed = StringUtil::FromChars<s32>(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<u32> parsed = StringUtil::FromChars<u32>(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<float> parsed = StringUtil::FromChars<float>(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<double> parsed = StringUtil::FromChars<double>(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<bool> parsed = StringUtil::FromChars<bool>(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<std::string> MemorySettingsInterface::GetStringList(const char* section, const char* key) const
{
std::vector<std::string> 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<std::string>& 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);
}

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include "core/settings.h" #include "heterogeneous_containers.h"
#include "settings_interface.h"
#include <string> #include <string>
#include <unordered_map>
class RegTestSettingsInterface final : public SettingsInterface class MemorySettingsInterface final : public SettingsInterface
{ {
public: public:
RegTestSettingsInterface(); MemorySettingsInterface();
~RegTestSettingsInterface(); ~MemorySettingsInterface();
bool Save() override; bool Save() override;
@ -44,6 +44,10 @@ public:
using SettingsInterface::GetUIntValue; using SettingsInterface::GetUIntValue;
private: private:
using KeyMap = std::unordered_map<std::string, std::string>; using KeyMap = UnorderedStringMultimap<std::string>;
KeyMap m_keys; using SectionMap = UnorderedStringMap<KeyMap>;
void SetValue(const char* section, const char* key, std::string value);
SectionMap m_sections;
}; };

View File

@ -3,8 +3,6 @@ add_executable(duckstation-regtest
regtest_host_display.h regtest_host_display.h
regtest_host_interface.cpp regtest_host_interface.cpp
regtest_host_interface.h regtest_host_interface.h
regtest_settings_interface.cpp
regtest_settings_interface.h
) )
target_link_libraries(duckstation-regtest PRIVATE core common frontend-common scmversion) target_link_libraries(duckstation-regtest PRIVATE core common frontend-common scmversion)

View File

@ -7,12 +7,10 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="regtest_host_display.cpp" /> <ClCompile Include="regtest_host_display.cpp" />
<ClCompile Include="regtest_host_interface.cpp" /> <ClCompile Include="regtest_host_interface.cpp" />
<ClCompile Include="regtest_settings_interface.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="regtest_host_display.h" /> <ClInclude Include="regtest_host_display.h" />
<ClInclude Include="regtest_host_interface.h" /> <ClInclude Include="regtest_host_interface.h" />
<ClInclude Include="regtest_settings_interface.h" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\dep\msvc\vsprops\ConsoleApplication.props" /> <Import Project="..\..\dep\msvc\vsprops\ConsoleApplication.props" />
<Import Project="..\frontend-common\frontend-common.props" /> <Import Project="..\frontend-common\frontend-common.props" />

View File

@ -3,11 +3,9 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="regtest_host_interface.cpp" /> <ClCompile Include="regtest_host_interface.cpp" />
<ClCompile Include="regtest_host_display.cpp" /> <ClCompile Include="regtest_host_display.cpp" />
<ClCompile Include="regtest_settings_interface.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="regtest_host_interface.h" /> <ClInclude Include="regtest_host_interface.h" />
<ClInclude Include="regtest_host_display.h" /> <ClInclude Include="regtest_host_display.h" />
<ClInclude Include="regtest_settings_interface.h" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "core/host_interface.h" #include "core/host_interface.h"
#include "regtest_settings_interface.h" #include "common/memory_settings_interface.h"
class RegTestHostInterface final : public HostInterface class RegTestHostInterface final : public HostInterface
{ {
@ -60,6 +60,6 @@ private:
void InitializeSettings(); void InitializeSettings();
void UpdateSettings(); void UpdateSettings();
RegTestSettingsInterface m_settings_interface; MemorySettingsInterface m_settings_interface;
std::recursive_mutex m_settings_mutex; std::recursive_mutex m_settings_mutex;
}; };

View File

@ -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<s32> parsed = StringUtil::FromChars<s32>(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<u32> parsed = StringUtil::FromChars<u32>(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<float> parsed = StringUtil::FromChars<float>(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<double> parsed = StringUtil::FromChars<double>(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<bool> parsed = StringUtil::FromChars<bool>(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<std::string> RegTestSettingsInterface::GetStringList(const char* section, const char* key) const
{
std::vector<std::string> ret;
Panic("Not implemented");
return ret;
}
void RegTestSettingsInterface::SetStringList(const char* section, const char* key,
const std::vector<std::string>& 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;
}
}