From 26f6cda58928cfd802bd13fcbfed737329ffacd8 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 24 Aug 2026 16:10:42 -0700 Subject: [PATCH] FEXCore/Config: Support Serializing Serializes every option, even ones that are set to default to ensure validation that if any config option value is added or changed that they are captured. Skips a handful of options that are either meta options, environment options that don't matter, or HostFeatures which is handled elsewhere. (HostFeatures will be controlling bucketing rather than the remaining options). This serialization is currently 1413 bytes and generates in 17580ns on my A1A. So it's not the fastest, definitely don't want to be generating it constantly per process. --- FEXCore/Source/Interface/Config/Config.cpp | 48 ++++++++++++++++++++++ FEXCore/include/FEXCore/Config/Config.h | 3 ++ 2 files changed, 51 insertions(+) diff --git a/FEXCore/Source/Interface/Config/Config.cpp b/FEXCore/Source/Interface/Config/Config.cpp index dba0ba687..0394e12d6 100644 --- a/FEXCore/Source/Interface/Config/Config.cpp +++ b/FEXCore/Source/Interface/Config/Config.cpp @@ -38,8 +38,16 @@ namespace detail { #define OPT_STRARRAY(group, enum, json, default) OPT_STR(group, enum, json, default) #define OPT_STRENUM(group, enum, json, default) const uint64_t P(enum) = FEXCore::ToUnderlying(P(default)); #include + constexpr static std::array option_names = { +#define OPT_BASE(type, group, enum, json, default) #json, +#include + }; } // namespace detail +std::string_view GetConfigJSONName(FEXCore::Config::ConfigOption option) { + return FEXCore::Config::detail::option_names[option]; +} + enum Paths { PATH_DATA_DIR_LOCAL = 0, PATH_DATA_DIR_GLOBAL, @@ -511,4 +519,44 @@ void Value::GetListIfExists(FEXCore::Config::ConfigOption Option, StringArray } } template void Value::GetListIfExists(FEXCore::Config::ConfigOption Option, StringArrayType* List); + +fextl::string SerializeForCache() { + fextl::string Config {}; + + auto append_string_triple = [](fextl::string& Config, std::string_view Key, ConfigOption Option, auto Value) { + Config.append(Key); + Config.append(1, '\0'); + Config.append(fextl::fmt::format("{}", FEXCore::ToUnderlying(Option))); + Config.append(1, '\0'); + Config.append(fextl::fmt::format("{}", Value)); + Config.append(1, '\0'); + }; + + const auto SerializeValue = [&Config, append_string_triple](auto ConfigVal, const auto Default) { + if constexpr (Option == ConfigOption::CONFIG_ENV || Option == ConfigOption::CONFIG_HOSTENV || Option == ConfigOption::CONFIG_ADDITIONALARGUMENTS || + Option == ConfigOption::CONFIG_APP_CONFIG_NAME || Option == ConfigOption::CONFIG_APP_FILENAME || + Option == ConfigOption::CONFIG_IS64BIT_MODE || Option == ConfigOption::CONFIG_INTERPRETER_INSTALLED || + Option == ConfigOption::CONFIG_DISABLE_VIXL_INDIRECT_RUNTIME_CALLS || Option == ConfigOption::CONFIG_HOSTFEATURES) { + // Skip environment variables, meta arguments, and HostFeatures. + return; + } + + append_string_triple(Config, FEXCore::Config::GetConfigJSONName(Option), Option, ConfigVal()); + }; + +#define OPT_BASE(type, group, enum, json, default) \ + SerializeValue.template operator()(FEXCore::Config::Get_##enum(), default); +#define OPT_STR(group, enum, json, default) \ + SerializeValue.template operator()(FEXCore::Config::Get_##enum(), default); +#define OPT_STRARRAY(group, enum, json, default) // Unsupported. +#define OPT_STRENUM(group, enum, json, default) // Unsupported. +#include + return Config; +} + +FEX_DEFAULT_VISIBILITY bool CheckConfigMatches(std::string_view Config) { + // Serialize current config and just check if it matches. + return SerializeForCache() == Config; +} + } // namespace FEXCore::Config diff --git a/FEXCore/include/FEXCore/Config/Config.h b/FEXCore/include/FEXCore/Config/Config.h index dae94634c..eb09f21a3 100644 --- a/FEXCore/include/FEXCore/Config/Config.h +++ b/FEXCore/include/FEXCore/Config/Config.h @@ -36,6 +36,7 @@ namespace Handler { enum ConfigOption { #define OPT_BASE(type, group, enum, json, default) CONFIG_##enum, #include + CONFIG_MAX, }; #define ENUMDEFINES @@ -230,6 +231,8 @@ FEX_DEFAULT_VISIBILITY std::optional GetConv(ConfigOption Option); FEX_DEFAULT_VISIBILITY std::optional Get(ConfigOption Option); FEX_DEFAULT_VISIBILITY void Set(ConfigOption Option, std::string_view Data); FEX_DEFAULT_VISIBILITY void Erase(ConfigOption Option); +FEX_DEFAULT_VISIBILITY fextl::string SerializeForCache(); +FEX_DEFAULT_VISIBILITY bool CheckConfigMatches(std::string_view Config); template class FEX_DEFAULT_VISIBILITY Value {