pureikyubu/src/json.h
2023-08-02 23:43:02 +03:00

257 lines
5.7 KiB
C++

// Json serialization engine. Json is used to store emulator settings, as well as for the JDI system (Json Debug Interface).
// All errors during deserialization are thrown by exceptions.
// During serializing, in theory, there shouldn't be any mistakes (if we are not enemies to ourselves and have not changed, for example, the Value type to some non-standard one).
#pragma once
class Json
{
// Foolproof
static const int MaxDepth = 255;
static const int MaxStringSize = 0x1000;
static const int MaxElements = 255; // Deserialize only
public:
class Value;
private:
// Recursive destruction of value and all its descendants
void DestroyValue(Value* value);
static wchar_t* CloneStr(const wchar_t* str);
static wchar_t* CloneAnsiStr(const char* str);
#pragma region "Serialization Related"
// Serialize context
struct SerializeContext
{
uint8_t** ptr;
size_t* actualSize;
size_t maxSize;
};
// Base procedure for generating the text.
static void EmitChar(SerializeContext* ctx, uint8_t val, bool sizeOnly);
static void EmitText(SerializeContext* ctx, const char* text, bool sizeOnly);
// Simple wchar_t to UTF-8 Converter + Escaping
static void EmitCodePoint(SerializeContext* ctx, int cp, bool sizeOnly);
static void EmitWcharString(SerializeContext* ctx, wchar_t* str, bool sizeOnly);
// Indentation
static void Indent(SerializeContext* ctx, int depth, bool sizeOnly);
#pragma endregion "Serialization Related"
#pragma region "De-Serialization Related"
// Deserialize
struct DeserializeContext
{
uint8_t* ptr;
size_t offset;
size_t maxSize;
};
static bool IsWhiteSpace(uint8_t value);
static bool IsControl(uint8_t value);
// Json Token
enum class TokenType : char
{
Unknown = '?',
EndOfStream = 'E',
ObjectStart = '{',
ObjectEnd = '}',
ArrayStart = '[',
ArrayEnd = ']',
Colon = ':',
Comma = ',',
Int = 'I',
Float = 'F',
True = 't',
False = 'f',
Null = 'N',
String = 'S',
};
class Token
{
public:
TokenType type = TokenType::Unknown;
union
{
uint64_t AsInt;
float AsFloat;
bool AsBool;
wchar_t* AsString;
} value;
Token()
{
value.AsString = nullptr;
}
};
static bool GetLiteral(DeserializeContext* ctx, Token& token);
static int FetchCodepoint(DeserializeContext* ctx);
static bool GetString(DeserializeContext* ctx, Token& token);
static bool IsAllowed(uint8_t val, char* allowed);
static bool GetFloat(DeserializeContext* ctx, Token& token);
static bool GetInt(DeserializeContext* ctx, Token& token);
static void GetToken(Token& token, DeserializeContext* ctx);
#pragma endregion "De-Serialization Related"
public:
// Json Value
enum class ValueType : char
{
Unknown = '?',
Object = 'O',
Array = 'A',
Int = 'I',
Float = 'F',
String = 'S',
Bool = 'B',
Null = 'N',
};
class Value
{
char* CloneName(const char* otherName);
char* CloneWcharName(const wchar_t* otherName);
void DeserializeObject(DeserializeContext* ctx);
void DeserializeArray(DeserializeContext* ctx);
public:
Value* parent = nullptr;
ValueType type = ValueType::Unknown;
// Name of object members.
// For simplicity, we do not support Non-Ansi Json value names (although full use of Utf-8 is allowed in the values)
char* name = nullptr;
union
{
float AsFloat;
uint64_t AsInt;
uint8_t AsUint8;
uint16_t AsUint16;
uint32_t AsUint32;
wchar_t* AsString;
bool AsBool;
} value;
std::list<Value*> children;
Value()
{
// Only the String value type requires the release of the stored value.
value.AsString = nullptr;
}
Value(Value* _parent)
{
// Only the String value type requires the release of the stored value.
value.AsString = nullptr;
this->parent = _parent;
}
~Value()
{
while (!children.empty())
{
Value* child = children.back();
children.pop_back();
delete child;
}
if (type == ValueType::String)
{
if (value.AsString)
{
delete[] value.AsString;
value.AsString = nullptr;
}
}
if (name != nullptr)
{
delete[] name;
name = nullptr;
}
}
void Serialize(SerializeContext* ctx, int depth, bool sizeOnly);
void Deserialize(DeserializeContext* ctx, wchar_t* keyName);
// Dynamic modification
Value* AddInt(const char* keyName, int _value);
Value* AddUInt16(const char* keyName, uint16_t _value);
Value* AddUInt32(const char* keyName, uint32_t _value);
Value* AddUInt64(const char* keyName, uint64_t _value);
Value* AddFloat(const char* keyName, float _value);
Value* AddNull(const char* keyName);
Value* AddBool(const char* keyName, bool _value);
Value* AddString(const char* keyName, const wchar_t* str);
Value* AddAnsiString(const char* keyName, const char* str);
Value* ReplaceString(const wchar_t* str);
Value* AddObject(const char* keyName);
Value* AddArray(const char* keyName);
Value* AddValue(const char* keyName, Value* value);
Value* Add(Value* _parent, Value* other);
Value* Replace(Value* _parent, Value* other);
// Access
Value* ByName(const char* byName);
Value* ByType(const ValueType byType);
void SetName(const char* newName)
{
if (name != nullptr)
{
delete[] name;
}
name = CloneName(newName);
}
};
/// <summary>
/// Deserialized root.
/// </summary>
Value root;
// Api
~Json()
{
DestroyValue(&root);
}
// The process of serialization is that we recursively walk through all the values, passing in each "running" context
// which has the pointer to the generated text and the current text size.
void Serialize(void* text, size_t maxTextSize, size_t& actualTextSize);
void GetSerializedTextSize(void* text, size_t maxTextSize, size_t& actualTextSize);
void Deserialize(void* text, size_t textSize);
// Clone
void Clone(Json* other);
// Merge
void Merge(Json* other);
};