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.
This commit is contained in:
Kawe Mazidjatari 2025-02-07 21:38:03 +01:00
parent 4188c13780
commit 18e71086b6
2 changed files with 153 additions and 90 deletions

View File

@ -62,7 +62,7 @@ void CBanSystem::LoadList(void)
rapidjson::Value::ConstMemberIterator entryIt; 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; const rapidjson::Value& entry = entryIt->value;

View File

@ -11,11 +11,11 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
enum class JSONFieldType_e enum class JSONFieldType_e
{ {
kInvalid = -1,
kNull = 0, kNull = 0,
kObject,
kBool, kBool,
kNumber,
kSint32, kSint32,
kUint32, kUint32,
@ -29,14 +29,52 @@ enum class JSONFieldType_e
kDouble, kDouble,
kLDouble, kLDouble,
kNumber,
kString, kString,
kArray kArray,
kObject,
}; };
template <class T>
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 // 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) 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 <class T>
inline bool JSON_TypeToString(const T& data)
{
return JSON_TypeToString(JSON_ExtractType(data));
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: checks if the member's value is of type provided // 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 <class T> template <class T>
inline bool JSON_IsOfType(const T& data, const JSONFieldType_e type) 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: case JSONFieldType_e::kNull:
return data.IsNull(); return data.IsNull();
case JSONFieldType_e::kObject:
return data.IsObject();
case JSONFieldType_e::kBool: case JSONFieldType_e::kBool:
return data.IsBool(); return data.IsBool();
case JSONFieldType_e::kNumber:
return data.IsNumber();
case JSONFieldType_e::kSint32: case JSONFieldType_e::kSint32:
return data.IsInt(); return data.IsInt();
case JSONFieldType_e::kUint32: case JSONFieldType_e::kUint32:
@ -82,10 +148,14 @@ inline bool JSON_IsOfType(const T& data, const JSONFieldType_e type)
return data.IsDouble(); return data.IsDouble();
case JSONFieldType_e::kLDouble: case JSONFieldType_e::kLDouble:
return data.IsLosslessDouble(); return data.IsLosslessDouble();
case JSONFieldType_e::kNumber:
return data.IsNumber();
case JSONFieldType_e::kString: case JSONFieldType_e::kString:
return data.IsString(); return data.IsString();
case JSONFieldType_e::kArray: case JSONFieldType_e::kArray:
return data.IsArray(); return data.IsArray();
case JSONFieldType_e::kObject:
return data.IsObject();
default: default:
return false; return false;
} }
@ -116,16 +186,16 @@ inline JSONFieldType_e JSON_GetTypeForType()
else if constexpr (std::is_same<T, std::string>::value) else if constexpr (std::is_same<T, std::string>::value)
return JSONFieldType_e::kString; return JSONFieldType_e::kString;
else else
static_assert(false, "Cannot classify data type; unsupported."); static_assert(std::is_same_v<T, void>, "Cannot classify data type; unsupported.");
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: checks if the member exists and if its value is of type provided // Purpose: checks if the member exists and if its value is of type provided
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class T> template <class T>
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()) if (it != data.MemberEnd())
{ {
@ -135,25 +205,63 @@ inline bool JSON_HasMemberAndIsOfType(const T& data, const char* const member, c
return false; return false;
} }
//-----------------------------------------------------------------------------
// Purpose: checks if the member exists, and sets 'out' to its iterator if the
// aforementioned condition is met
//-----------------------------------------------------------------------------
template <class T>
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 <class T>
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, // Purpose: checks if the member exists and if its value is of specified type,
// and sets 'out' to its value if all aforementioned conditions // and sets 'out' to its value if all aforementioned conditions
// are met // are met
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class T, class V> template <class T, class V>
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; out = it->value.Get<V>();
return true;
if (JSON_IsOfType(val, type))
{
out = val.Get<V>();
return true;
}
} }
// Not found or didn't match specified type. // 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 // and sets 'out' to its value if all aforementioned conditions are met
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class T, class V> template <class T, class V>
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<V>(), it))
{ {
const rapidjson::Value& val = it->value; out = it->value.Get<V>();
return true;
if (JSON_IsOfType(val, JSON_GetTypeForType<V>()))
{
out = val.Get<V>();
return true;
}
} }
// Not found or didn't match classified type. // Not found or didn't match classified type.
return false; return false;
} }
template <class T> template <class T>
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; 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 // else the provided default gets returned
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class T, class V> template <class T, class V>
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; V val;
@ -215,47 +318,25 @@ inline V JSON_GetValueOrDefault(const T& data, const char* const member, const V
return def; return def;
} }
//----------------------------------------------------------------------------- template <class V>
// Purpose: checks if the member exists and if its value is of type provided, inline bool JSON_StringToNumber(const char* const str, const size_t len, V& num)
// and sets 'out' to its iterator if all aforementioned conditions
// are met
//-----------------------------------------------------------------------------
template <class T>
inline bool JSON_GetIterator(const T& data, const char* const member,
const JSONFieldType_e type, typename T::ConstMemberIterator& out)
{ {
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<V, int32_t>::value) || (std::is_same<V, int64_t>::value) ||
(std::is_same<V, uint32_t>::value) || (std::is_same<V, uint64_t>::value))
{ {
if (JSON_IsOfType(it->value, type)) result = std::from_chars(str, end, num, 0);
{
out = it;
return true;
}
} }
else if constexpr ((std::is_same<V, float>::value) || (std::is_same<V, double>::value))
// 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 <class T>
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())
{ {
out = it; result = std::from_chars(str, end, num, std::chars_format::general);
return true;
} }
else
static_assert(std::is_same_v<V, void>, "Cannot classify numeric type; unsupported.");
// Not found. return (result.ptr == end) && (result.ec == std::errc());
return false;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -272,37 +353,19 @@ inline bool JSON_ParseNumber(const T& data, V& num)
} }
else if (JSON_IsOfType(data, JSONFieldType_e::kString)) else if (JSON_IsOfType(data, JSONFieldType_e::kString))
{ {
const char* const string = data.GetString(); return JSON_StringToNumber(data.GetString(), data.GetStringLength(), num);
char* end = nullptr;
if constexpr (std::is_same<V, int32_t>::value)
num = strtol(string, &end, 0);
else if constexpr (std::is_same<V, int64_t>::value)
num = strtoll(string, &end, 0);
else if constexpr (std::is_same<V, uint32_t>::value)
num = strtoul(string, &end, 0);
else if constexpr (std::is_same<V, uint64_t>::value)
num = strtoull(string, &end, 0);
else if constexpr (std::is_same<V, float>::value)
num = static_cast<float>(strtod(string, &end));
else if constexpr (std::is_same<V, double>::value)
num = strtod(string, &end);
else
static_assert(false, "Cannot classify numeric type; unsupported.");
return end == &string[data.GetStringLength()];
} }
return false; return false;
} }
template <class T, class V> template <class T, class V>
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; rapidjson::Document::ConstMemberIterator it;
if (JSON_GetIterator(data, member, it)) if (JSON_GetIterator(data, member, it))
{ {
return JSON_ParseNumber(it->value, num);; return JSON_ParseNumber(it->value, num);
} }
return false; 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 // else the provided default gets returned
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <class T, class V> template <class T, class V>
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; V num;