From 18e71086b6e2b30434abdd60212a040c7243382d Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 7 Feb 2025 21:38:03 +0100 Subject: [PATCH] Tier2: strong optimizations for JSON tools - Fetch field name strlen at compile time using rapidjson's string refs. - Use from_chars to do string to number conversions which isn't locale aware, for maximum speed. --- src/networksystem/bansystem.cpp | 2 +- src/public/tier2/jsonutils.h | 241 ++++++++++++++++++++------------ 2 files changed, 153 insertions(+), 90 deletions(-) diff --git a/src/networksystem/bansystem.cpp b/src/networksystem/bansystem.cpp index ffe3acd7..be4fdbec 100644 --- a/src/networksystem/bansystem.cpp +++ b/src/networksystem/bansystem.cpp @@ -62,7 +62,7 @@ void CBanSystem::LoadList(void) rapidjson::Value::ConstMemberIterator entryIt; - if (JSON_GetIterator(document, idx, JSONFieldType_e::kObject, entryIt)) + if (JSON_GetIterator(document, rapidjson::StringRef(idx, strlen(idx)), JSONFieldType_e::kObject, entryIt)) { const rapidjson::Value& entry = entryIt->value; diff --git a/src/public/tier2/jsonutils.h b/src/public/tier2/jsonutils.h index 58ade569..bd5184e2 100644 --- a/src/public/tier2/jsonutils.h +++ b/src/public/tier2/jsonutils.h @@ -11,11 +11,11 @@ //----------------------------------------------------------------------------- enum class JSONFieldType_e { + kInvalid = -1, + kNull = 0, - kObject, kBool, - kNumber, kSint32, kUint32, @@ -29,14 +29,52 @@ enum class JSONFieldType_e kDouble, kLDouble, + kNumber, + kString, - kArray + kArray, + kObject, }; +template +inline JSONFieldType_e JSON_ExtractType(const T& data) +{ + if (data.IsNull()) + return JSONFieldType_e::kNull; + if (data.IsBool()) + return JSONFieldType_e::kBool; + if (data.IsInt()) + return JSONFieldType_e::kSint32; + if (data.IsUint()) + return JSONFieldType_e::kUint32; + if (data.IsInt64()) + return JSONFieldType_e::kSint64; + if (data.IsUint64()) + return JSONFieldType_e::kUint64; + if (data.IsFloat()) + return JSONFieldType_e::kFloat; + if (data.IsLosslessFloat()) + return JSONFieldType_e::kLFloat; + if (data.IsDouble()) + return JSONFieldType_e::kDouble; + if (data.IsLosslessDouble()) + return JSONFieldType_e::kLDouble; + if (data.IsNumber()) + return JSONFieldType_e::kNumber; + if (data.IsString()) + return JSONFieldType_e::kString; + if (data.IsArray()) + return JSONFieldType_e::kArray; + if (data.IsObject()) + return JSONFieldType_e::kObject; + + return JSONFieldType_e::kInvalid; +} + //----------------------------------------------------------------------------- // Purpose: gets the object type as string //----------------------------------------------------------------------------- -inline const char* JSON_TypeToString(const rapidjson::Type type) +inline const char* JSON_InternalTypeToString(const rapidjson::Type type) { switch (type) { @@ -50,8 +88,40 @@ inline const char* JSON_TypeToString(const rapidjson::Type type) } } +inline const char* JSON_TypeToString(const JSONFieldType_e type) +{ + switch (type) + { + case JSONFieldType_e::kNull: return "null"; + case JSONFieldType_e::kBool: return "bool"; + case JSONFieldType_e::kSint32: return "signed int32"; + case JSONFieldType_e::kUint32: return "unsigned int32"; + case JSONFieldType_e::kSint64: return "signed int64"; + case JSONFieldType_e::kUint64: return "unsigned int64"; + case JSONFieldType_e::kFloat: return "float"; + case JSONFieldType_e::kLFloat: return "lossless float"; + case JSONFieldType_e::kDouble: return "double"; + case JSONFieldType_e::kLDouble: return "lossless double"; + case JSONFieldType_e::kNumber: return "number"; + case JSONFieldType_e::kString: return "string"; + case JSONFieldType_e::kArray: return "array"; + case JSONFieldType_e::kObject: return "object"; + default: return "unknown"; + } +} + +template +inline bool JSON_TypeToString(const T& data) +{ + return JSON_TypeToString(JSON_ExtractType(data)); +} + //----------------------------------------------------------------------------- // Purpose: checks if the member's value is of type provided +// NOTE : the switch case was done intentionally instead of JSON_ExtractType +// on the object as this function gets used in most accessors that +// check on types, and this approach is faster as we already know the +// type beforehand //----------------------------------------------------------------------------- template inline bool JSON_IsOfType(const T& data, const JSONFieldType_e type) @@ -60,12 +130,8 @@ inline bool JSON_IsOfType(const T& data, const JSONFieldType_e type) { case JSONFieldType_e::kNull: return data.IsNull(); - case JSONFieldType_e::kObject: - return data.IsObject(); case JSONFieldType_e::kBool: return data.IsBool(); - case JSONFieldType_e::kNumber: - return data.IsNumber(); case JSONFieldType_e::kSint32: return data.IsInt(); case JSONFieldType_e::kUint32: @@ -82,10 +148,14 @@ inline bool JSON_IsOfType(const T& data, const JSONFieldType_e type) return data.IsDouble(); case JSONFieldType_e::kLDouble: return data.IsLosslessDouble(); + case JSONFieldType_e::kNumber: + return data.IsNumber(); case JSONFieldType_e::kString: return data.IsString(); case JSONFieldType_e::kArray: return data.IsArray(); + case JSONFieldType_e::kObject: + return data.IsObject(); default: return false; } @@ -116,16 +186,16 @@ inline JSONFieldType_e JSON_GetTypeForType() else if constexpr (std::is_same::value) return JSONFieldType_e::kString; else - static_assert(false, "Cannot classify data type; unsupported."); + static_assert(std::is_same_v, "Cannot classify data type; unsupported."); } //----------------------------------------------------------------------------- // Purpose: checks if the member exists and if its value is of type provided //----------------------------------------------------------------------------- template -inline bool JSON_HasMemberAndIsOfType(const T& data, const char* const member, const JSONFieldType_e type) +inline bool JSON_HasMemberAndIsOfType(const T& data, typename T::StringRefType member, const JSONFieldType_e type) { - const T::ConstMemberIterator it = data.FindMember(member); + const T::ConstMemberIterator it = data.FindMember(rapidjson::Value(member)); if (it != data.MemberEnd()) { @@ -135,25 +205,63 @@ inline bool JSON_HasMemberAndIsOfType(const T& data, const char* const member, c return false; } +//----------------------------------------------------------------------------- +// Purpose: checks if the member exists, and sets 'out' to its iterator if the +// aforementioned condition is met +//----------------------------------------------------------------------------- +template +inline bool JSON_GetIterator(const T& data, typename T::StringRefType member, typename T::ConstMemberIterator& out) +{ + const T::ConstMemberIterator it = data.FindMember(rapidjson::Value(member)); + + if (it != data.MemberEnd()) + { + out = it; + return true; + } + + // Not found. + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if the member exists and if its value is of type provided, +// and sets 'out' to its iterator if all aforementioned conditions +// are met +//----------------------------------------------------------------------------- +template +inline bool JSON_GetIterator(const T& data, typename T::StringRefType member, + const JSONFieldType_e type, typename T::ConstMemberIterator& out) +{ + const T::ConstMemberIterator it = data.FindMember(rapidjson::Value(member)); + + if (it != data.MemberEnd()) + { + if (JSON_IsOfType(it->value, type)) + { + out = it; + return true; + } + } + + // Not found or didn't match specified type. + return false; +} + //----------------------------------------------------------------------------- // Purpose: checks if the member exists and if its value is of specified type, // and sets 'out' to its value if all aforementioned conditions // are met //----------------------------------------------------------------------------- template -inline bool JSON_GetValue(const T& data, const char* const member, const JSONFieldType_e type, V& out) +inline bool JSON_GetValue(const T& data, typename T::StringRefType member, const JSONFieldType_e type, V& out) { - const T::ConstMemberIterator it = data.FindMember(member); + rapidjson::Document::ConstMemberIterator it; - if (it != data.MemberEnd()) + if (JSON_GetIterator(data, member, type, it)) { - const rapidjson::Value& val = it->value; - - if (JSON_IsOfType(val, type)) - { - out = val.Get(); - return true; - } + out = it->value.Get(); + return true; } // Not found or didn't match specified type. @@ -165,26 +273,21 @@ inline bool JSON_GetValue(const T& data, const char* const member, const JSONFie // and sets 'out' to its value if all aforementioned conditions are met //----------------------------------------------------------------------------- template -inline bool JSON_GetValue(const T& data, const char* const member, V& out) +inline bool JSON_GetValue(const T& data, typename T::StringRefType member, V& out) { - const T::ConstMemberIterator it = data.FindMember(member); + rapidjson::Document::ConstMemberIterator it; - if (it != data.MemberEnd()) + if (JSON_GetIterator(data, member, JSON_GetTypeForType(), it)) { - const rapidjson::Value& val = it->value; - - if (JSON_IsOfType(val, JSON_GetTypeForType())) - { - out = val.Get(); - return true; - } + out = it->value.Get(); + return true; } // Not found or didn't match classified type. return false; } template -inline bool JSON_GetValue(const T& data, const char* const member, std::string& out) +inline bool JSON_GetValue(const T& data, typename T::StringRefType member, std::string& out) { const char* stringVal; @@ -203,7 +306,7 @@ inline bool JSON_GetValue(const T& data, const char* const member, std::string& // else the provided default gets returned //----------------------------------------------------------------------------- template -inline V JSON_GetValueOrDefault(const T& data, const char* const member, const V def) +inline V JSON_GetValueOrDefault(const T& data, typename T::StringRefType member, const V def) { V val; @@ -215,47 +318,25 @@ inline V JSON_GetValueOrDefault(const T& data, const char* const member, const V return def; } -//----------------------------------------------------------------------------- -// Purpose: checks if the member exists and if its value is of type provided, -// and sets 'out' to its iterator if all aforementioned conditions -// are met -//----------------------------------------------------------------------------- -template -inline bool JSON_GetIterator(const T& data, const char* const member, - const JSONFieldType_e type, typename T::ConstMemberIterator& out) +template +inline bool JSON_StringToNumber(const char* const str, const size_t len, V& num) { - const T::ConstMemberIterator it = data.FindMember(member); + const char* const end = &str[len]; + std::from_chars_result result; - if (it != data.MemberEnd()) + if constexpr ((std::is_same::value) || (std::is_same::value) || + (std::is_same::value) || (std::is_same::value)) { - if (JSON_IsOfType(it->value, type)) - { - out = it; - return true; - } + result = std::from_chars(str, end, num, 0); } - - // Not found or didn't match specified type. - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: checks if the member exists, and sets 'out' to its iterator if the -// aforementioned condition is met -//----------------------------------------------------------------------------- -template -inline bool JSON_GetIterator(const T& data, const char* const member, typename T::ConstMemberIterator& out) -{ - const T::ConstMemberIterator it = data.FindMember(member); - - if (it != data.MemberEnd()) + else if constexpr ((std::is_same::value) || (std::is_same::value)) { - out = it; - return true; + result = std::from_chars(str, end, num, std::chars_format::general); } + else + static_assert(std::is_same_v, "Cannot classify numeric type; unsupported."); - // Not found. - return false; + return (result.ptr == end) && (result.ec == std::errc()); } //----------------------------------------------------------------------------- @@ -272,37 +353,19 @@ inline bool JSON_ParseNumber(const T& data, V& num) } else if (JSON_IsOfType(data, JSONFieldType_e::kString)) { - const char* const string = data.GetString(); - char* end = nullptr; - - if constexpr (std::is_same::value) - num = strtol(string, &end, 0); - else if constexpr (std::is_same::value) - num = strtoll(string, &end, 0); - else if constexpr (std::is_same::value) - num = strtoul(string, &end, 0); - else if constexpr (std::is_same::value) - num = strtoull(string, &end, 0); - else if constexpr (std::is_same::value) - num = static_cast(strtod(string, &end)); - else if constexpr (std::is_same::value) - num = strtod(string, &end); - else - static_assert(false, "Cannot classify numeric type; unsupported."); - - return end == &string[data.GetStringLength()]; + return JSON_StringToNumber(data.GetString(), data.GetStringLength(), num); } return false; } template -inline bool JSON_ParseNumber(const T& data, const char* const member, V& num) +inline bool JSON_ParseNumber(const T& data, typename T::StringRefType member, V& num) { rapidjson::Document::ConstMemberIterator it; if (JSON_GetIterator(data, member, it)) { - return JSON_ParseNumber(it->value, num);; + return JSON_ParseNumber(it->value, num); } return false; @@ -314,7 +377,7 @@ inline bool JSON_ParseNumber(const T& data, const char* const member, V& num) // else the provided default gets returned //----------------------------------------------------------------------------- template -inline V JSON_GetNumberOrDefault(const T& data, const char* const member, V def) +inline V JSON_GetNumberOrDefault(const T& data, typename T::StringRefType member, V def) { V num;