3#include "Foundation/Logging/Logger.h"
5#include <glm/gtc/type_ptr.hpp>
17 const bool traceParsing =
false;
19 void parseOrdinalDate(
int& day,
int& month,
int year) {
20 int dayCounts[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
22 if ((!(year % 4) && (year % 100)) || !(year % 400)) {
25 for (
int idx = 0; idx < 12; ++idx) {
28 if (day <= dayCounts[idx]) {
31 day -= dayCounts[idx];
36const size_t Cogs::Core::ParsedDataTypeSizes[] = {
59static_assert(std::size(Cogs::Core::ParsedDataTypeSizes) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
61const char * Cogs::Core::ParsedDataTypeNames[] = {
84static_assert(std::size(Cogs::Core::ParsedDataTypeNames) == (size_t(Cogs::Core::ParsedDataType::Buffer) + 1));
86Cogs::Core::ParsedValue::ParsedValue(
const ParsedValue & other)
93 float4x4Value = other.float4x4Value;
94 expressions = other.expressions;
95 values = other.values;
98Cogs::Core::ParsedValue::ParsedValue(ParsedValue && other)
noexcept
100 std::swap(key, other.key);
101 std::swap(value, other.value);
103 dimension = other.dimension;
104 valueHash = other.valueHash;
105 float4x4Value = other.float4x4Value;
106 std::swap(expressions, other.expressions);
107 std::swap(values, other.values);
115 dimension = other.dimension;
116 valueHash = other.valueHash;
117 float4x4Value = other.float4x4Value;
118 expressions = other.expressions;
119 values = other.values;
125bool Cogs::Core::ParsedValue::asBool(
bool& rv,
bool complain)
const
129 case Cogs::Core::ParsedDataType::String:
133 c =
static_cast<decltype(lc)::value_type
>(std::tolower(c));
148 case Cogs::Core::ParsedDataType::Float:
149 rv = floatValue != 0.f;
153 case Cogs::Core::ParsedDataType::Double:
154 rv = doubleValue != 0.f;
158 case Cogs::Core::ParsedDataType::Int:
162 case Cogs::Core::ParsedDataType::UInt:
167 case Cogs::Core::ParsedDataType::Bool:
177 LOG_ERROR(logger,
"Unable to interpret '%s' as bool.", value.c_str());
182bool Cogs::Core::ParsedValue::asFloat(
float& rv,
bool complain)
const
186 case Cogs::Core::ParsedDataType::Float:
190 case Cogs::Core::ParsedDataType::Double:
191 rv =
static_cast<float>(doubleValue);
194 case Cogs::Core::ParsedDataType::Int:
195 rv =
static_cast<float>(intValue);
198 case Cogs::Core::ParsedDataType::UInt:
199 rv =
static_cast<float>(uintValue);
202 case Cogs::Core::ParsedDataType::Bool:
203 rv = boolValue ? 1.f : 0.f;
212 LOG_ERROR(logger,
"Unable to interpret '%s' as float.", value.c_str());
217bool Cogs::Core::ParsedValue::asInt(
int& rv,
bool complain)
const
221 case Cogs::Core::ParsedDataType::Float:
222 rv =
static_cast<int>(floatValue);
225 case Cogs::Core::ParsedDataType::Double:
226 rv =
static_cast<int>(doubleValue);
229 case Cogs::Core::ParsedDataType::Int:
233 case Cogs::Core::ParsedDataType::UInt:
237 case Cogs::Core::ParsedDataType::Bool:
238 rv = boolValue ? 1 : 0;
246 LOG_ERROR(logger,
"Unable to interpret '%s' as int.", value.c_str());
253bool Cogs::Core::matchChars(
const char c,
const StringView & chars)
255 const size_t count = chars.
length();
256 const char *
const data = chars.
data();
258 for (
size_t i = 0; i < count; ++i) {
259 if (c == data[i])
return true;
267 const char * data = str.
data();
268 const size_t len = str.
size();
269 const char *
const end = data + len;
271 const char * token =
nullptr;
273 while (data != end) {
274 const bool isSplitter = matchChars(*data, splits);
276 if (!isSplitter && !token) {
278 }
else if (isSplitter && token) {
279 tokens.push_back(
StringView(token, data - token));
286 if (token) tokens.push_back(
StringView(token, data - token));
289void Cogs::Core::split(
const StringView & str,
const StringView & splits,
const int grouper, TokenStream& tokens)
291 const char * data = str.
data();
292 const size_t len = str.
size();
293 const char *
const end = data + len;
295 const char * token =
nullptr;
297 bool inGroup =
false;
298 while (data != end) {
300 if (*data == grouper) {
303 const bool isSplitter = !inGroup && matchChars(*data, splits);
305 if (!isSplitter && !token) {
308 else if (isSplitter && token) {
309 tokens.push_back(
StringView(token, data - token));
316 if (token) tokens.push_back(
StringView(token, data - token));
324 split(str, splits, tokens);
328Cogs::Core::TokenStream Cogs::Core::tokenize(
const StringView & valueStr)
333 split(valueStr,
" \t{},", tokens);
337void Cogs::Core::tokenize(
const StringView & valueStr, TokenStream & tokens)
339 split(valueStr,
" \t{},", tokens);
342void Cogs::Core::parseStringValue(
const StringView & value, ParsedValue & v)
344 thread_local static std::vector<StringView> tokens;
347 split(value,
" \t,{}[]",
'$', tokens);
349 if (tokens.empty()) {
350 LOG_ERROR(logger,
"Invalid value specification.");
352 }
else if (tokens.size() == 1) {
355 if (tokens[0] ==
"true") {
356 v.type = ParsedDataType::Bool;
358 }
else if (tokens[0] ==
"false") {
359 v.type = ParsedDataType::Bool;
362 v.type = ParsedDataType::String;
369 v.valueHash = tokens[1].hash();
371 size_t type_hash = tokens[0].hash();
372 tokens.erase(tokens.begin());
373 v.value = tokens[0].to_string();
378 v.type = ParsedDataType::String;
382 v.type = ParsedDataType::Float;
383 parseValues(v.expressions, &v.floatValue, tokens, 1);
386 v.type = ParsedDataType::Float2;
387 parseValues(v.expressions, glm::value_ptr(v.float2Value), tokens, 2);
390 v.type = ParsedDataType::Float3;
391 parseValues(v.expressions, glm::value_ptr(v.float3Value), tokens, 3);
394 v.type = ParsedDataType::Float4;
395 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 4);
399 v.type = ParsedDataType::Float4x4;
400 parseValues(v.expressions, glm::value_ptr(v.float4Value), tokens, 16);
404 v.type = ParsedDataType::Int;
405 parseValues(v.expressions, &v.intValue, tokens, 1);
408 v.type = ParsedDataType::Int2;
409 parseValues(v.expressions, glm::value_ptr(v.int2Value), tokens, 2);
412 v.type = ParsedDataType::Int3;
413 parseValues(v.expressions, glm::value_ptr(v.int3Value), tokens, 3);
416 v.type = ParsedDataType::Int4;
417 parseValues(v.expressions, glm::value_ptr(v.int4Value), tokens, 4);
421 v.type = ParsedDataType::UInt;
422 parseValues(v.expressions, &v.uintValue, tokens, 1);
425 v.type = ParsedDataType::UInt2;
426 parseValues(v.expressions, glm::value_ptr(v.uint2Value), tokens, 2);
429 v.type = ParsedDataType::UInt3;
430 parseValues(v.expressions, glm::value_ptr(v.uint3Value), tokens, 3);
433 v.type = ParsedDataType::UInt4;
434 parseValues(v.expressions, glm::value_ptr(v.uint4Value), tokens, 4);
438 v.type = ParsedDataType::Double;
439 parseValues(v.expressions, &v.doubleValue, tokens, 1);
443 v.type = ParsedDataType::Bool;
444 parseValues(v.expressions, &v.boolValue, tokens, 1);
449 v.type = ParsedDataType::Texture2D;
450 v.value = tokens[0].to_string();
451 for (
size_t i = 1, n = tokens.size(); i < n; i++) {
454 v.texture.flags |= ParsedValueTextureFlags::DepthTexture;
457 v.texture.flags |= ParsedValueTextureFlags::LinearSampler;
460 v.texture.flags &= ~ParsedValueTextureFlags::LinearSampler;
463 v.texture.dataType = ParsedDataType::Float;
466 v.texture.dataType = ParsedDataType::Float2;
469 v.texture.dataType = ParsedDataType::Float3;
472 v.texture.dataType = ParsedDataType::Float4;
475 v.texture.dataType = ParsedDataType::Int;
478 v.texture.dataType = ParsedDataType::Int2;
481 v.texture.dataType = ParsedDataType::Int3;
484 v.texture.dataType = ParsedDataType::Int4;
487 v.texture.dataType = ParsedDataType::UInt;
490 v.texture.dataType = ParsedDataType::UInt2;
493 v.texture.dataType = ParsedDataType::UInt3;
496 v.texture.dataType = ParsedDataType::UInt4;
500 LOG_ERROR(logger,
"Could not determine number of samples");
504 std::string_view sv = tokens[i].to_string_view();
505 auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), samples);
506 if (ec == std::errc()) {
507 v.texture.samples = samples;
510 LOG_ERROR(logger,
"Could not determine number of samples \"%.*s\" is not a number", StringViewFormat(tokens[i]));
517 v.type = ParsedDataType::Buffer;
518 v.value = tokens[0].to_string();
519 for (
size_t i = 1, n = tokens.size(); i < n; i++) {
522 v.buffer.flags |= ParsedValueBufferFlags::Structured;
525 v.buffer.flags &= ~ParsedValueBufferFlags::Structured;
528 v.buffer.dataType = ParsedDataType::Float;
531 v.buffer.dataType = ParsedDataType::Float2;
534 v.buffer.dataType = ParsedDataType::Float3;
537 v.buffer.dataType = ParsedDataType::Float4;
540 v.buffer.dataType = ParsedDataType::Int;
543 v.buffer.dataType = ParsedDataType::Int2;
546 v.buffer.dataType = ParsedDataType::Int3;
549 v.buffer.dataType = ParsedDataType::Int4;
552 v.buffer.dataType = ParsedDataType::UInt;
555 v.buffer.dataType = ParsedDataType::UInt2;
558 v.buffer.dataType = ParsedDataType::UInt3;
561 v.buffer.dataType = ParsedDataType::UInt4;
564 LOG_WARNING(logger,
"Unrecognized Buffer keyword \"%.*s\"", StringViewFormat(tokens[i]));
570 v.type = ParsedDataType::ConstantBuffer;
571 v.value = tokens[0].to_string();
574 v.type = ParsedDataType::String;
580Cogs::TextureFormat Cogs::Core::parseTextureFormat(
const StringView & format, Cogs::TextureFormat defaultFormat)
582 if (format.
empty())
return defaultFormat;
584 if (Cogs::TextureFormat textureFormat = Cogs::parseDataFormat(format); textureFormat != Cogs::TextureFormat::Unknown) {
585 return textureFormat;
588 LOG_ERROR(logger,
"Error parsing texture format: %.*s. Reverting to default.", StringViewFormat(format));
589 return defaultFormat;
592double Cogs::Core::parseDouble(
const StringView & token,
double defaultValue)
594 if (!token.
size())
return defaultValue;
596 return std::atof(token.
to_string().c_str());
599float Cogs::Core::parseFloat(
const StringView & token,
float defaultValue)
601 return static_cast<float>(parseDouble(token, defaultValue));
604int32_t Cogs::Core::parseInt(
const StringView & token, int32_t defaultValue)
606 if (!token.
size())
return defaultValue;
608 if (token.
size() > 2 && token[0] ==
'0' && (token[1] ==
'x' || token[1] ==
'X')) {
609 return std::strtol(token.
to_string().c_str(),
nullptr, 16);
611 return std::strtol(token.
to_string().c_str(),
nullptr, 10);
615uint32_t Cogs::Core::parseUInt(
const StringView & token, uint32_t defaultValue)
617 if (!token.
size())
return defaultValue;
619 if (token.
size() > 2 && token[0] ==
'0' && (token[1] ==
'x' || token[1] ==
'X')) {
620 return std::strtoul(token.
to_string().c_str(),
nullptr, 16);
622 return std::strtoul(token.
to_string().c_str(),
nullptr, 10);
626bool Cogs::Core::parseBool(
const StringView & token,
bool )
628 switch(token.
hash()) {
654 LOG_ERROR(logger,
"Failed to parse \"%.*s\" as bool", StringViewFormat(token));
661 if (!token.
size())
return false;
663 std::from_chars_result rc = std::from_chars(token.
data(),
666 if (rc.ec != std::errc()) {
667 LOG_ERROR(logger,
"Failed to parse \"%.*s\" as int: %s",
668 StringViewFormat(token),
669 std::make_error_code(rc.ec).message().c_str());
681 size_t t = iso8601.
find(
"T");
683 uint32_t milliseconds = 0;
689 assert(iso8601[0] !=
'P');
696 TokenStream tzTokens = split(time,
"-+Z");
697 TokenStream dateTokens = split(date,
"-");
698 TokenStream timeTokens = split(tzTokens[0],
":");
700 switch (dateTokens.size()) {
706 if (iso8601[0] ==
'-') {
708 tm.tm_mon = parseInt(dateTokens[0].substr(0, 2));
709 tm.tm_mday = parseInt(dateTokens[0].substr(2));
711 else if (dateTokens[0].size() == 8) {
713 tm.tm_year = parseInt(dateTokens[0].substr(0, 4));
714 tm.tm_mon = parseInt(dateTokens[0].substr(4, 2));
715 tm.tm_mday = parseInt(dateTokens[0].substr(6));
717 else if (dateTokens[0].size() == 7) {
719 tm.tm_year = parseInt(dateTokens[0].substr(0, 4));
720 tm.tm_mday = parseInt(dateTokens[0].substr(4));
722 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
727 if (iso8601[0] ==
'-') {
729 tm.tm_mon = parseInt(dateTokens[0]);
730 tm.tm_mday = parseInt(dateTokens[1]);
735 assert(dateTokens[1][0] !=
'W');
737 if (dateTokens[1].size() == 3) {
739 tm.tm_year = parseInt(dateTokens[0]);
740 tm.tm_mday = parseInt(dateTokens[1]);
742 parseOrdinalDate(tm.tm_mday, tm.tm_mon, tm.tm_year);
746 tm.tm_year = parseInt(dateTokens[0]);
747 tm.tm_mon = parseInt(dateTokens[1]);
755 assert (dateTokens[1][0] !=
'W');
758 tm.tm_year = parseInt(dateTokens[0]);
759 tm.tm_mon = parseInt(dateTokens[1]);
760 tm.tm_mday = parseInt(dateTokens[2]);
765 switch (timeTokens.size()) {
770 tm.tm_hour = parseInt(timeTokens[0].substr(0, 2));
772 if ((timeTokens[0][2] !=
'.') && (timeTokens[0][2] !=
',')) {
773 tm.tm_min = parseInt(timeTokens[0].substr(2, 2));
775 if ((timeTokens[0][4] !=
'.') && (timeTokens[0][4] !=
',')) {
776 tm.tm_sec = parseInt(timeTokens[0].substr(4, 2));
778 if ((timeTokens[0][6] ==
'.') || (timeTokens[0][6] ==
',')) {
797 tm.tm_hour = parseInt(timeTokens[0]);
798 tm.tm_min = parseInt(timeTokens[1]);
800 if ((timeTokens[1][2] ==
'.') || (timeTokens[1][2] ==
',')) {
809 tm.tm_hour = parseInt(timeTokens[0]);
810 tm.tm_min = parseInt(timeTokens[1]);
811 tm.tm_sec = parseInt(timeTokens[2]);
813 if ((timeTokens[2][2] ==
'.') || (timeTokens[2][2] ==
',')) {
820 if (tzTokens.size() == 2) {
821 TokenStream parts = split(tzTokens[1],
":");
825 switch (parts.size()) {
827 tzHours = parseInt(parts[0].substr(0, 2));
828 tzMinutes = parseInt(parts[0].substr(2, 2));
832 tzHours = parseInt(parts[0]);
833 tzMinutes = parseInt(parts[1]);
836 tzOffset = (tzHours * 3600) + (tzMinutes * 60) * 1000;
838 if (tzTokens[1][0] ==
'-') {
839 tzOffset = -tzOffset;
850 std::time_t now = std::time(0);
852 tm.tm_year = std::localtime(&now)->tm_year;
860 std::time_t basetime = mktime(&tm);
863 tm = *std::localtime(&basetime);
867 tm = *std::gmtime(&basetime);
871 return ((basetime + diff) * 1000) + milliseconds;
880 thread_local static std::vector<StringView> tokens;
882 split(token,
"| ", tokens);
884 if (tokens.size() > 1) {
886 for (
auto & t : tokens) {
892 LOG_ERROR(logger,
"Could not find enumerator \"%.*s\" in %s.", StringViewFormat(t), type.
getName().
c_str());
904 LOG_ERROR(logger,
"Could not find enumerator \"%.*s\" in %s.", StringViewFormat(token), type.
getName().
c_str());
912 bool isCharacter(
char c)
914 return ((c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z'));
916 bool isNumber(
char c)
918 return (c >=
'0' && c <=
'9');
921 int parseEnumFlags(
const char *& current,
const char * end,
const Reflection::Type & type,
int defaultValue)
925 bool isLogicalAnd =
false;
926 while (current != end) {
927 if (*current ==
'~') {
930 }
else if (*current ==
'(') {
931 auto scopeValue = parseEnumFlags(++current, end, type, defaultValue);
934 value &= flip ? ~scopeValue : scopeValue;
936 value |= flip ? ~scopeValue : scopeValue;
938 }
else if (*current ==
')') {
941 }
else if (*current ==
'|') {
942 isLogicalAnd =
false;
944 }
else if (*current ==
'&') {
947 }
else if (isNumber(*current)) {
948 char* endptr =
nullptr;
949 value = strtoul(current, &endptr, 0);
951 }
else if (isCharacter(*current)) {
952 auto tokenFirst = current;
953 const char * tokenLast = tokenFirst;
954 const char * tokenCurrent = tokenFirst;
956 while (tokenCurrent && tokenCurrent != end) {
957 if (isCharacter(*tokenCurrent) || (tokenCurrent!=tokenFirst && isNumber(*tokenCurrent))) {
959 tokenLast = tokenCurrent;
961 tokenCurrent =
nullptr;
965 auto scopeValue = parseEnum(
StringView(tokenFirst, tokenLast - tokenFirst), type, defaultValue);
967 value &= flip ? ~scopeValue : scopeValue;
969 value |= flip ? ~scopeValue : scopeValue;
972 }
else if (current != end) {
983 if (token.
empty())
return defaultValue;
985 auto begin = token.
begin();
986 return parseEnumFlags(begin, token.
end(), type, defaultValue);
993 template<
typename T,
typename Func>
994 void parseValues(T * values,
const TokenStream & tokens,
size_t expected, Func f)
996 if (tokens.size() < expected) {
997 if (traceParsing) LOG_DEBUG(logger,
"Expected %zu values, got %zu.\n", expected, tokens.size());
1000 const auto count = std::min(tokens.size(), expected);
1002 for (
size_t i = 0; i < count; ++i) {
1003 values[i] = f(tokens[i], T());
1007 template<
typename T,
typename Func>
1008 void parseValues(std::vector<std::pair<size_t, std::string>>& expressions, T * values,
const TokenStream & tokens,
size_t expected, Func f)
1010 if (tokens.size() < expected) {
1011 if (traceParsing) LOG_DEBUG(logger,
"Expected %zu values, got %zu.\n", expected, tokens.size());
1014 const auto count = std::min(tokens.size(), expected);
1016 for (
size_t i = 0; i < count; ++i) {
1017 const auto & token = tokens[0];
1019 if (!token.
empty() && *token.
begin() ==
'$') {
1020 auto end = token.
find(
"$", 1);
1022 expressions.push_back(std::make_pair(i, token.
substr(1, end).
to_string()));
1027 values[i] = f(tokens[i], T());
1036void Cogs::Core::parseQueryString(std::vector<ParsedValue>& attributes,
StringView query)
1040 a = a + (a == 0 ? 0 : 1);
1041 auto b = query.
find(
"&", a);
1043 if (attributeValue.length()) {
1044 auto sepLoc = attributeValue.
find(
"=");
1045 auto key = attributeValue.substr(0, sepLoc);
1047 ParsedValue attribute;
1048 attribute.key = key.to_string();
1050 parseStringValue(
"true", attribute);
1053 parseStringValue(attributeValue.substr(sepLoc + 1), attribute);
1055 attributes.push_back(attribute);
1062void Cogs::Core::parseValues(
float * values,
const TokenStream & tokens,
size_t expected)
1064 parseValues(values, tokens, expected, parseFloat);
1067void Cogs::Core::parseValues(
double * values,
const TokenStream & tokens,
size_t expected)
1069 parseValues(values, tokens, expected, parseDouble);
1072void Cogs::Core::parseValues(
int * values,
const TokenStream & tokens,
size_t expected)
1074 parseValues(values, tokens, expected, parseInt);
1077void Cogs::Core::parseValues(uint32_t * values,
const TokenStream & tokens,
size_t expected)
1079 parseValues(values, tokens, expected, parseUInt);
1082void Cogs::Core::parseValues(
bool * values,
const TokenStream & tokens,
size_t expected)
1084 parseValues(values, tokens, expected, parseBool);
1088void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
float * values,
const TokenStream & tokens,
size_t expected)
1090 parseValues(expressions, values, tokens, expected, parseFloat);
1093void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
double * values,
const TokenStream & tokens,
size_t expected)
1095 parseValues(expressions, values, tokens, expected, parseFloat);
1098void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
int * values,
const TokenStream & tokens,
size_t expected)
1100 parseValues(expressions, values, tokens, expected, parseInt);
1103void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions, uint32_t * values,
const TokenStream & tokens,
size_t expected)
1105 parseValues(expressions, values, tokens, expected, parseUInt);
1108void Cogs::Core::parseValues(std::vector<std::pair<size_t, std::string>>& expressions,
bool * values,
const TokenStream & tokens,
size_t expected)
1110 parseValues(expressions, values, tokens, expected, parseBool);
Log implementation class.
Represents a discrete type definition, describing a native type class.
const Enumerator * getEnumerator(const Name &name) const
Get a pointer to the enumerator with the given name.
bool isEnum() const
Get if the type is an enumeration type.
constexpr const Name & getName() const
Get the unique name of the type.
Provides a weakly referenced view over the contents of a string.
constexpr const char * data() const noexcept
Get the sequence of characters referenced by the string view.
constexpr iterator begin() noexcept
Iterator to the beginning of the string.
constexpr size_t size() const noexcept
Get the size of the string.
constexpr iterator end() noexcept
Iterator to the end of the string.
constexpr size_t length() const noexcept
Get the length of the string.
constexpr bool empty() const noexcept
Check if the string is empty.
constexpr StringView substr(size_t offset, size_t count=NoPosition) const noexcept
Get the given sub string.
size_t hashLowercase(size_t hashValue=Cogs::hash()) const noexcept
Get the hash code of the string converted to lowercase.
static constexpr size_t NoPosition
No position.
size_t find(const StringView &other, size_t offset=0) const noexcept
Find the given string segment inside the string.
std::string to_string() const
String conversion method.
constexpr size_t hash() const noexcept
Get the hash code of the string.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool parseBool_(bool &rv, const StringView &token)
Parse a bool putting return value in rv and returning whether or not parsing was successful.
uint64_t COGSCORE_DLL_API parseISO8601(const StringView &iso8601)
Parse ISO 8601 timestamps.
bool parseInt_(int32_t &rv, const StringView &token)
Parse an int putting return value in rv and returning whether or not parsing was successful.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
COGSFOUNDATION_API size_t hashLowercase(std::string_view str, size_t hashValue=Cogs::hash()) noexcept
Get the hash code of the string converted to lowercase.
Stores the parsed output of a key/value pair.
std::string key
The input key.
std::string value
The value is available for all non vector types.
size_t dimension
This value is for external use.
size_t valueHash
The hash is only set for values where the type is explicitly given.
ParsedDataType type
The data type of the value. Determines type stored in union below.
int getValue() const
Get the value of the enumerator.
const char * c_str() const
Gets the name as a null-terminated string.