296 lines
8.3 KiB
C++
296 lines
8.3 KiB
C++
#pragma once
|
|
|
|
#include <Orange/Core/Variant.h>
|
|
#include <Orange/Core/Vector.h>
|
|
#include <Orange/Core/Parse.h>
|
|
#include <Orange/Core/Span.h>
|
|
#include <Orange/Graphics/Color.h>
|
|
|
|
#include <unordered_map>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <memory>
|
|
|
|
namespace orange::kv
|
|
{
|
|
namespace Types
|
|
{
|
|
enum KVVariantType
|
|
{
|
|
None,
|
|
String,
|
|
Int,
|
|
Float,
|
|
Ptr,
|
|
Color,
|
|
Uint64,
|
|
KeyValues,
|
|
|
|
Count,
|
|
};
|
|
}
|
|
using KeyValuesType = Types::KVVariantType;
|
|
|
|
class KeyValues;
|
|
|
|
|
|
class KeyValuesVariant
|
|
{
|
|
public:
|
|
using KeyValuesChild = std::unique_ptr<KeyValues>;
|
|
using KeyValuesString = SmallVector<char, 8>;
|
|
|
|
KeyValuesVariant()
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
KeyValuesVariant(const T& arg)
|
|
{
|
|
Set(arg);
|
|
}
|
|
|
|
template <typename T>
|
|
KeyValuesVariant(T&& arg)
|
|
{
|
|
Set(Move(arg));
|
|
}
|
|
|
|
~KeyValuesVariant()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case Types::String:
|
|
m_str.clear();
|
|
break;
|
|
case Types::KeyValues:
|
|
m_data.Destruct<KeyValuesChild>();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
m_type = Types::None;
|
|
m_data.Clear();
|
|
}
|
|
|
|
template <typename T>
|
|
T Get() const;
|
|
|
|
void Set(const std::string& val) { Clear(); m_type = Types::String; m_str = KeyValuesString{ StringView{ val } }; }
|
|
void Set(StringView val) { Clear(); m_type = Types::String; m_str = KeyValuesString{ val }; }
|
|
void Set(int32_t val) { Clear(); m_type = Types::Int; m_data.Get<int64_t>() = val; }
|
|
void Set(int64_t val) { Clear(); m_type = Types::Int; m_data.Get<int64_t>() = val; }
|
|
void Set(float val) { Clear(); m_type = Types::Float; m_data.Get<double>() = val; }
|
|
void Set(double val) { Clear(); m_type = Types::Float; m_data.Get<double>() = val; }
|
|
void Set(void* val) { Clear(); m_type = Types::Ptr; m_data.Get<void*>() = val; }
|
|
void Set(RGBAColor32 val) { Clear(); m_type = Types::Color; m_data.Construct<RGBAColor32>(val); }
|
|
void Set(uint32_t val) { Clear(); m_type = Types::Uint64; m_data.Get<uint64_t>() = val; }
|
|
void Set(uint64_t val) { Clear(); m_type = Types::Uint64; m_data.Get<uint64_t>() = val; }
|
|
void Set(KeyValuesChild val) { Clear(); m_type = Types::KeyValues; m_data.Get<KeyValuesChild>() = Move(val); }
|
|
|
|
KeyValuesVariant& operator [](const char *string);
|
|
|
|
const KeyValuesVariant& operator [](const char *string) const;
|
|
|
|
static KeyValuesVariant& GetEmptyValue() { return s_Nothing; }
|
|
private:
|
|
static KeyValuesVariant s_Nothing;
|
|
|
|
KeyValuesType m_type = Types::None;
|
|
|
|
KeyValuesString m_str;
|
|
|
|
Variant<
|
|
int64_t,
|
|
double,
|
|
void*,
|
|
uint64_t,
|
|
RGBAColor32,
|
|
KeyValuesChild> m_data;
|
|
};
|
|
|
|
class KeyValues
|
|
{
|
|
public:
|
|
static std::unique_ptr<KeyValues> ParseFromUTF8(StringView buffer)
|
|
{
|
|
const char *start = buffer.begin();
|
|
const char *end = buffer.end();
|
|
return ParseChild(start, end);
|
|
}
|
|
|
|
static KeyValues& Nothing()
|
|
{
|
|
return s_Nothing;
|
|
}
|
|
|
|
KeyValuesVariant& operator [](const char *string)
|
|
{
|
|
auto iter = m_children.find(string);
|
|
if (iter == m_children.end())
|
|
{
|
|
std::cerr << "Returning KeyValuesVariant empty value: " << string << std::endl;
|
|
return KeyValuesVariant::GetEmptyValue();
|
|
}
|
|
|
|
return iter->second;
|
|
}
|
|
|
|
const KeyValuesVariant& operator [] (const char *string) const
|
|
{
|
|
auto iter = m_children.find(string);
|
|
if (iter == m_children.end())
|
|
{
|
|
std::cerr << "Returning KeyValuesVariant empty value: " << string << std::endl;
|
|
return KeyValuesVariant::GetEmptyValue();
|
|
}
|
|
|
|
return iter->second;
|
|
}
|
|
private:
|
|
static KeyValues s_Nothing;
|
|
|
|
static std::unique_ptr<KeyValues> ParseChild(const char*& start, const char* end)
|
|
{
|
|
StringView range = StringView(start, end);
|
|
|
|
if (start == end)
|
|
return nullptr;
|
|
|
|
stream::ConsumeSpaceAndNewLine(start, end);
|
|
|
|
if (*start != '{')
|
|
return nullptr;
|
|
|
|
auto kv = std::make_unique<KeyValues>();
|
|
|
|
bool inString = false;
|
|
|
|
bool fillingInValue = false;
|
|
std::string key;
|
|
std::string value;
|
|
|
|
start++;
|
|
while (start != end)
|
|
{
|
|
if (stream::IsStringToken(range, start))
|
|
{
|
|
inString = !inString;
|
|
start++;
|
|
continue;
|
|
}
|
|
|
|
if (!inString)
|
|
{
|
|
if (stream::IsCPPComment(range, start))
|
|
{
|
|
stream::AdvancePast(start, end, "\r\n");
|
|
stream::ConsumeSpaceAndNewLine(start, end);
|
|
continue;
|
|
}
|
|
|
|
if (*start == '{')
|
|
{
|
|
fprintf(stdout, "Adding block child: %s - {\n", key.c_str());
|
|
auto child = ParseChild(start, end);
|
|
kv->m_children.emplace(key, Move(child));
|
|
fprintf(stdout, "Adding block child: }\n");
|
|
|
|
fillingInValue = false;
|
|
key.clear();
|
|
value.clear();
|
|
}
|
|
|
|
if (*start == '}')
|
|
{
|
|
start++;
|
|
break;
|
|
}
|
|
|
|
if (*start == ' ' || *start == '\t' || *start == '\n' || *start == '\r')
|
|
{
|
|
fillingInValue = !fillingInValue;
|
|
|
|
if (!fillingInValue)
|
|
{
|
|
fprintf(stdout, "Adding child: %s - %s\n", key.c_str(), value.c_str());
|
|
kv->m_children.emplace(key, value);
|
|
key.clear();
|
|
value.clear();
|
|
}
|
|
else if (*start == '\n' || *start == '\r')
|
|
{
|
|
fillingInValue = false;
|
|
}
|
|
|
|
stream::ConsumeSpaceAndNewLine(start, end);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fillingInValue)
|
|
value.push_back(*start);
|
|
else
|
|
key.push_back(*start);
|
|
|
|
start++;
|
|
}
|
|
|
|
return kv;
|
|
}
|
|
|
|
std::unordered_multimap<std::string, KeyValuesVariant> m_children;
|
|
};
|
|
|
|
template <>
|
|
inline StringView KeyValuesVariant::Get() const
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case Types::String:
|
|
return m_str;
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
template <>
|
|
inline KeyValues& KeyValuesVariant::Get() const
|
|
{
|
|
switch (m_type)
|
|
{
|
|
case Types::KeyValues:
|
|
{
|
|
auto value = m_data.Get<KeyValuesChild>().get();
|
|
if (value)
|
|
return *value;
|
|
}
|
|
[[fallthrough]];
|
|
default:
|
|
return KeyValues::Nothing();
|
|
}
|
|
}
|
|
|
|
inline KeyValuesVariant& KeyValuesVariant::operator [](const char *string)
|
|
{
|
|
if (m_type != Types::KeyValues)
|
|
abort();
|
|
|
|
return Get<kv::KeyValues&>()[string];
|
|
}
|
|
|
|
inline const KeyValuesVariant& KeyValuesVariant::operator [](const char *string) const
|
|
{
|
|
if (m_type != Types::KeyValues)
|
|
abort();
|
|
|
|
return Get<const kv::KeyValues&>()[string];
|
|
}
|
|
}
|