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;
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;

View File

@ -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 <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
//-----------------------------------------------------------------------------
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 <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
// 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>
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<T, std::string>::value)
return JSONFieldType_e::kString;
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
//-----------------------------------------------------------------------------
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())
{
@ -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 <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,
// and sets 'out' to its value if all aforementioned conditions
// are met
//-----------------------------------------------------------------------------
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;
if (JSON_IsOfType(val, type))
{
out = val.Get<V>();
return true;
}
out = it->value.Get<V>();
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 <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;
if (JSON_IsOfType(val, JSON_GetTypeForType<V>()))
{
out = val.Get<V>();
return true;
}
out = it->value.Get<V>();
return true;
}
// Not found or didn't match classified type.
return false;
}
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;
@ -203,7 +306,7 @@ inline bool JSON_GetValue(const T& data, const char* const member, std::string&
// else the provided default gets returned
//-----------------------------------------------------------------------------
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;
@ -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 <class T>
inline bool JSON_GetIterator(const T& data, const char* const member,
const JSONFieldType_e type, typename T::ConstMemberIterator& out)
template <class V>
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<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))
{
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 <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())
else if constexpr ((std::is_same<V, float>::value) || (std::is_same<V, double>::value))
{
out = it;
return true;
result = std::from_chars(str, end, num, std::chars_format::general);
}
else
static_assert(std::is_same_v<V, void>, "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<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 JSON_StringToNumber(data.GetString(), data.GetStringLength(), num);
}
return false;
}
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;
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 <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;