pureikyubu/SRC/Common/Json.cpp
2020-04-19 15:57:35 +03:00

1082 lines
21 KiB
C++

#include "pch.h"
void Json::DestroyValue(Value* value)
{
while (!value->children.empty())
{
Value* child = value->children.back();
value->children.pop_back();
DestroyValue(child);
delete child;
}
if (value->type == ValueType::String)
{
if (value->value.AsString)
{
delete[] value->value.AsString;
}
}
if (value->name != nullptr)
{
delete[] value->name;
}
}
TCHAR* Json::CloneStr(const TCHAR* str)
{
size_t len = _tcslen(str);
TCHAR* clone = new TCHAR[len + 1];
_tcscpy_s(clone, len + 1, str);
return clone;
}
TCHAR* Json::CloneAnsiStr(const char* str)
{
size_t len = strlen(str);
TCHAR* clone = new TCHAR[len + 1];
TCHAR* tcharPtr = clone;
char* charPtr = (char*)str;
while (*charPtr)
{
*tcharPtr++ = *charPtr++;
}
*tcharPtr++ = 0;
return clone;
}
#pragma region "Serialization Related"
void Json::EmitChar(SerializeContext* ctx, uint8_t val, bool sizeOnly)
{
assert(*ctx->actualSize < ctx->maxSize);
if (!sizeOnly)
{
(*ctx->ptr)[0] = val;
(*ctx->ptr)++;
}
(*ctx->actualSize)++;
}
void Json::EmitText(SerializeContext* ctx, const char* text, bool sizeOnly)
{
uint8_t* ptr = (uint8_t*)text;
while (*ptr)
EmitChar(ctx, *ptr++, sizeOnly);
}
void Json::EmitCodePoint(SerializeContext* ctx, int cp, bool sizeOnly)
{
uint8_t c[4] = { 0 };
int utf8Size = 0;
// http://www.zedwood.com/article/cpp-utf8-char-to-codepoint
if (cp <= 0x7F) { c[0] = (uint8_t)cp; utf8Size = 1; }
else if (cp <= 0x7FF) { c[0] = (cp >> 6) + 192; c[1] = (cp & 63) + 128; utf8Size = 2; }
else if (0xd800 <= cp && cp <= 0xdfff) { throw "Invalid block of utf8"; }
else if (cp <= 0xFFFF) { c[0] = (cp >> 12) + 224; c[1] = ((cp >> 6) & 63) + 128; c[2] = (cp & 63) + 128; utf8Size = 3; }
else if (cp <= 0x10FFFF) { c[0] = (cp >> 18) + 240; c[1] = ((cp >> 12) & 63) + 128; c[2] = ((cp >> 6) & 63) + 128; c[3] = (cp & 63) + 128; utf8Size = 4; }
else { throw "Unsupported codepoint range"; }
for (int i = 0; i < utf8Size; i++)
{
EmitChar(ctx, c[i], sizeOnly);
}
}
void Json::EmitTcharString(SerializeContext* ctx, TCHAR* str, bool sizeOnly)
{
TCHAR* ptr = str;
while (*ptr)
{
int cp = (int)*ptr;
// Escaping
switch (cp)
{
case '\"':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, '\"', sizeOnly);
break;
case '\\':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, '\\', sizeOnly);
break;
case '/':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, '/', sizeOnly);
break;
case '\b':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, 'b', sizeOnly);
break;
case '\f':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, 'f', sizeOnly);
break;
case '\n':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, 'n', sizeOnly);
break;
case '\r':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, 'r', sizeOnly);
break;
case '\t':
EmitCodePoint(ctx, '\\', sizeOnly);
EmitCodePoint(ctx, 't', sizeOnly);
break;
default:
EmitCodePoint(ctx, cp, sizeOnly);
}
ptr++;
}
}
// Indentation
void Json::Indent(SerializeContext* ctx, int depth, bool sizeOnly)
{
while (depth--)
Json::EmitText(ctx, " ", sizeOnly);
}
#pragma endregion "Serialization Related"
#pragma region "De-Serialization Related"
bool Json::IsWhiteSpace(uint8_t value)
{
switch (value)
{
case ' ':
case '\r':
case '\n':
case '\t':
return true;
}
return false;
}
bool Json::IsControl(uint8_t value)
{
switch (value)
{
case '{':
case '}':
case '[':
case ']':
case ':':
case ',':
return true;
}
return false;
}
bool Json::GetLiteral(Json::DeserializeContext* ctx, Token& token)
{
// :p
if (ctx->offset < (ctx->maxSize - 4))
{
if (ctx->ptr[0] == 'n' && ctx->ptr[1] == 'u' && ctx->ptr[2] == 'l' && ctx->ptr[3] == 'l')
{
token.type = TokenType::Null;
ctx->ptr += 4;
ctx->offset += 4;
return true;
}
else if (ctx->ptr[0] == 't' && ctx->ptr[1] == 'r' && ctx->ptr[2] == 'u' && ctx->ptr[3] == 'e')
{
token.type = TokenType::True;
ctx->ptr += 4;
ctx->offset += 4;
return true;
}
}
if (ctx->offset < (ctx->maxSize - 5))
{
if (ctx->ptr[0] == 'f' && ctx->ptr[1] == 'a' && ctx->ptr[2] == 'l' && ctx->ptr[3] == 's' && ctx->ptr[4] == 'e')
{
token.type = TokenType::False;
ctx->ptr += 5;
ctx->offset += 5;
return true;
}
}
return false;
}
int Json::FetchCodepoint(DeserializeContext* ctx)
{
// http://www.zedwood.com/article/cpp-utf8-char-to-codepoint
assert(ctx->offset < ctx->maxSize);
unsigned char u0 = ctx->ptr[0]; if (u0 >= 0 && u0 <= 127)
{
ctx->offset++;
ctx->ptr++;
return u0;
}
ctx->offset++;
ctx->ptr++;
assert(ctx->offset < ctx->maxSize);
unsigned char u1 = ctx->ptr[0]; if (u0 >= 192 && u0 <= 223)
{
ctx->offset++;
ctx->ptr++;
return (u0 - 192) * 64 + (u1 - 128);
}
ctx->offset++;
ctx->ptr++;
if (u0 == 0xed && (u1 & 0xa0) == 0xa0) throw "code points, 0xd800 to 0xdfff";
assert(ctx->offset < ctx->maxSize);
unsigned char u2 = ctx->ptr[0]; if (u0 >= 224 && u0 <= 239)
{
ctx->offset++;
ctx->ptr++;
return (u0 - 224) * 4096 + (u1 - 128) * 64 + (u2 - 128);
}
ctx->offset++;
ctx->ptr++;
assert(ctx->offset < ctx->maxSize);
unsigned char u3 = ctx->ptr[0]; if (u0 >= 240 && u0 <= 247)
{
ctx->offset++;
ctx->ptr++;
return (u0 - 240) * 262144 + (u1 - 128) * 4096 + (u2 - 128) * 64 + (u3 - 128);
}
ctx->offset++;
ctx->ptr++;
throw "Invalid codepoint range";
}
bool Json::GetString(DeserializeContext* ctx, Token& token)
{
TCHAR str[MaxStringSize] = { 0, };
size_t strSize = 0;
if (ctx->ptr[0] != '\"')
return false;
ctx->offset++;
ctx->ptr++;
while (ctx->offset < ctx->maxSize)
{
assert(strSize < MaxStringSize);
int cp = FetchCodepoint(ctx);
// End of string?
if (cp == '\"')
{
str[strSize] = 0;
token.type = TokenType::String;
token.value.AsString = CloneStr(str);
return true;
}
// Unescaping
if (cp == '\\')
{
cp = FetchCodepoint(ctx);
switch (cp)
{
case '\"': cp = '\"'; break;
case '\\': cp = '\\'; break;
case '/': cp = '/'; break;
case 'b': cp = '\b'; break;
case 'f': cp = '\f'; break;
case 'n': cp = '\n'; break;
case 'r': cp = '\r'; break;
case 't': cp = '\t'; break;
case 'u': throw "uXXXX not supported";
default: throw "Invalid escape sequence";
}
}
str[strSize++] = (TCHAR)cp;
}
return false;
}
bool Json::IsAllowed(uint8_t val, char* allowed)
{
char* ptr = allowed;
while (*ptr)
{
if (*ptr == val)
return true;
ptr++;
}
return false;
}
bool Json::GetFloat(DeserializeContext* ctx, Token& token)
{
char allowedChars[] = "0123456789.eE-";
char number[0x100] = { 0, };
int numberLen = 0;
size_t offset = 0;
while (offset < (ctx->maxSize - ctx->offset))
{
assert(numberLen < (sizeof(number) - 1));
if (IsWhiteSpace(ctx->ptr[offset]) || IsControl(ctx->ptr[offset]))
{
break;
}
if (!IsAllowed(ctx->ptr[offset], allowedChars))
{
return false;
}
number[numberLen++] = ctx->ptr[offset++];
}
if (numberLen != 0)
{
number[numberLen] = 0;
token.type = TokenType::Float;
token.value.AsFloat = (float)atof(number);
ctx->offset += numberLen;
ctx->ptr += numberLen;
return true;
}
return false;
}
bool Json::GetInt(DeserializeContext* ctx, Token& token)
{
char allowedChars[] = "0123456789";
char number[0x100] = { 0, };
int numberLen = 0;
size_t offset = 0;
while (offset < (ctx->maxSize - ctx->offset))
{
assert(numberLen < (sizeof(number) - 1));
if (IsWhiteSpace(ctx->ptr[offset]) || IsControl(ctx->ptr[offset]))
{
break;
}
if (!IsAllowed(ctx->ptr[offset], allowedChars))
{
return false;
}
number[numberLen++] = ctx->ptr[offset++];
}
if (numberLen != 0)
{
number[numberLen] = 0;
token.type = TokenType::Int;
token.value.AsInt = strtoull(number, nullptr, 10);
ctx->offset += numberLen;
ctx->ptr += numberLen;
return true;
}
return false;
}
void Json::GetToken(Token& token, DeserializeContext* ctx)
{
while (ctx->offset < ctx->maxSize)
{
if (IsWhiteSpace(ctx->ptr[0]))
{
ctx->ptr++;
ctx->offset++;
}
else break;
}
if (ctx->offset >= ctx->maxSize)
{
token.type = TokenType::EndOfStream;
return;
}
switch (ctx->ptr[0])
{
case '{':
token.type = TokenType::ObjectStart;
ctx->ptr++;
ctx->offset++;
return;
case '}':
token.type = TokenType::ObjectEnd;
ctx->ptr++;
ctx->offset++;
return;
case '[':
token.type = TokenType::ArrayStart;
ctx->ptr++;
ctx->offset++;
return;
case ']':
token.type = TokenType::ArrayEnd;
ctx->ptr++;
ctx->offset++;
return;
case ':':
token.type = TokenType::Colon;
ctx->ptr++;
ctx->offset++;
return;
case ',':
token.type = TokenType::Comma;
ctx->ptr++;
ctx->offset++;
return;
}
// Try get literal
if (GetLiteral(ctx, token))
return;
// Try get String
if (GetString(ctx, token))
return;
// Try get Int
if (GetInt(ctx, token))
return;
// Try get Float
if (GetFloat(ctx, token))
return;
throw "Unknown Token!";
}
#pragma endregion "De-Serialization Related"
char* Json::Value::CloneName(const char* otherName)
{
if (otherName == nullptr) // Name can be null
return nullptr;
size_t len = strlen(otherName);
char* clone = new char[len + 1];
strcpy_s(clone, len + 1, otherName);
return clone;
}
char* Json::Value::CloneTcharName(const TCHAR* otherName)
{
if (otherName == nullptr) // Name can be null
return nullptr;
size_t len = _tcslen(otherName);
char* clone = new char[len + 1];
for (size_t i = 0; i < len; i++)
{
clone[i] = (char)otherName[i];
}
clone[len] = 0;
return clone;
}
void Json::Value::DeserializeObject(DeserializeContext* ctx)
{
Token token, colon, comma;
type = ValueType::Object;
int counter = 0;
while (true)
{
Value* child = nullptr;
assert(counter < MaxElements);
Json::GetToken(token, ctx);
switch (token.type)
{
case TokenType::String:
Json::GetToken(colon, ctx);
assert(colon.type == TokenType::Colon);
child = new Value(this);
children.push_back(child);
child->Deserialize(ctx, token.value.AsString);
if (token.value.AsString != nullptr)
{
delete[] token.value.AsString;
}
counter++;
Json::GetToken(comma, ctx);
if (comma.type == TokenType::Comma)
{
break;
}
else if (comma.type == TokenType::ObjectEnd)
{
return;
}
else
{
throw "Json Object Syntax Error";
}
break;
case TokenType::ObjectEnd:
return;
}
}
}
void Json::Value::DeserializeArray(DeserializeContext* ctx)
{
Token token;
type = ValueType::Array;
int counter = 0;
while (true)
{
assert(counter < MaxElements);
// Check empty arrays
while (ctx->offset < ctx->maxSize)
{
if (IsWhiteSpace(ctx->ptr[0]))
{
ctx->ptr++;
ctx->offset++;
}
else break;
}
if (ctx->offset >= ctx->maxSize)
{
throw "Json Array Syntax Error";
}
if (ctx->ptr[0] == ']')
{
Json::GetToken(token, ctx); // Eat end array token
break;
}
// Array element
Value* child = new Value(this);
children.push_back(child);
child->Deserialize(ctx, nullptr);
counter++;
Json::GetToken(token, ctx);
switch (token.type)
{
case TokenType::Comma:
break;
case TokenType::ArrayEnd:
return;
default:
throw "Json Array Syntax Error";
}
}
}
void Json::Value::Serialize(SerializeContext* ctx, int depth, bool sizeOnly)
{
TCHAR temp[0x100] = { 0, };
assert(depth < MaxDepth);
switch (type)
{
case ValueType::Object:
Indent(ctx, depth, sizeOnly);
Json::EmitText(ctx, "{\r\n", sizeOnly);
for (auto it = children.begin(); it != children.end(); ++it)
{
if (it != children.begin())
{
Json::EmitText(ctx, ",\r\n", sizeOnly);
}
Value* child = *it;
Indent(ctx, depth + 1, sizeOnly);
Json::EmitChar(ctx, '\"', sizeOnly);
Json::EmitText(ctx, child->name, sizeOnly);
Json::EmitChar(ctx, '\"', sizeOnly);
Json::EmitText(ctx, " : ", sizeOnly);
child->Serialize(ctx, depth + 1, sizeOnly);
}
Indent(ctx, depth, sizeOnly);
Json::EmitText(ctx, "}\r\n", sizeOnly);
break;
case ValueType::Array:
Indent(ctx, depth, sizeOnly);
Json::EmitText(ctx, "[ ", sizeOnly);
for (auto it = children.begin(); it != children.end(); ++it)
{
if (it != children.begin())
{
Json::EmitText(ctx, ", ", sizeOnly);
}
Value* child = *it;
child->Serialize(ctx, depth + 1, sizeOnly);
}
Indent(ctx, depth, sizeOnly);
Json::EmitChar(ctx, ']', sizeOnly);
break;
case ValueType::Null:
Json::EmitText(ctx, "null", sizeOnly);
break;
case ValueType::Bool:
Json::EmitText(ctx, value.AsBool ? "true" : "false", sizeOnly);
break;
case ValueType::Int:
_stprintf_s(temp, _countof(temp) - 1, _T("%I64u"), value.AsInt);
EmitTcharString(ctx, temp, sizeOnly);
break;
case ValueType::Float:
_stprintf_s(temp, _countof(temp) - 1, _T("%.4f"), value.AsFloat);
EmitTcharString(ctx, temp, sizeOnly);
break;
case ValueType::String:
Json::EmitChar(ctx, '\"', sizeOnly);
EmitTcharString(ctx, value.AsString, sizeOnly);
Json::EmitChar(ctx, '\"', sizeOnly);
break;
default:
throw "Unknown ValueType";
}
}
void Json::Value::Deserialize(DeserializeContext* ctx, TCHAR* keyName)
{
Token token, key;
this->name = CloneTcharName(keyName);
Json::GetToken(token, ctx);
switch (token.type)
{
case TokenType::ObjectStart:
DeserializeObject(ctx);
break;
case TokenType::ArrayStart:
DeserializeArray(ctx);
break;
case TokenType::String:
type = ValueType::String;
value.AsString = CloneStr(token.value.AsString);
if (token.value.AsString != nullptr)
{
delete[] token.value.AsString;
}
break;
case TokenType::Int:
type = ValueType::Int;
value.AsInt = token.value.AsInt;
break;
case TokenType::Float:
type = ValueType::Float;
value.AsFloat = token.value.AsFloat;
break;
case TokenType::True:
type = ValueType::Bool;
value.AsBool = true;
break;
case TokenType::False:
type = ValueType::Bool;
value.AsBool = false;
break;
case TokenType::Null:
type = ValueType::Null;
break;
default:
throw "Json Syntax Error";
break;
}
}
// Dynamic modification
Json::Value* Json::Value::AddInt(const char* keyName, int _value)
{
Value* child = new Value(this);
child->type = ValueType::Int;
child->name = CloneName(keyName);
child->value.AsInt = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddUInt16(const char* keyName, uint16_t _value)
{
Value* child = new Value(this);
child->type = ValueType::Int;
child->name = CloneName(keyName);
child->value.AsInt = 0;
child->value.AsUint16 = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddUInt32(const char* keyName, uint32_t _value)
{
Value* child = new Value(this);
child->type = ValueType::Int;
child->name = CloneName(keyName);
child->value.AsInt = 0;
child->value.AsUint32 = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddUInt64(const char* keyName, uint64_t _value)
{
Value* child = new Value(this);
child->type = ValueType::Int;
child->name = CloneName(keyName);
child->value.AsInt = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddFloat(const char* keyName, float _value)
{
Value* child = new Value(this);
child->type = ValueType::Float;
child->name = CloneName(keyName);
child->value.AsFloat = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddNull(const char* keyName)
{
Value* child = new Value(this);
child->type = ValueType::Null;
child->name = CloneName(keyName);
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddBool(const char* keyName, bool _value)
{
Value* child = new Value(this);
child->type = ValueType::Bool;
child->name = CloneName(keyName);
child->value.AsBool = _value;
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddString(const char* keyName, const TCHAR* str)
{
Value* child = new Value(this);
child->type = ValueType::String;
child->name = CloneName(keyName);
child->value.AsString = CloneStr(str);
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddAnsiString(const char* keyName, const char* str)
{
Value* child = new Value(this);
child->type = ValueType::String;
child->name = CloneName(keyName);
child->value.AsString = CloneAnsiStr(str);
children.push_back(child);
return child;
}
Json::Value* Json::Value::ReplaceString(const TCHAR* str)
{
assert(type == ValueType::String);
if (value.AsString)
{
delete[] value.AsString;
}
value.AsString = CloneStr(str);
return this;
}
Json::Value* Json::Value::AddObject(const char* keyName)
{
Value* child = new Value(this);
child->type = ValueType::Object;
child->name = CloneName(keyName);
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddArray(const char* keyName)
{
Value* child = new Value(this);
child->type = ValueType::Array;
child->name = CloneName(keyName);
children.push_back(child);
return child;
}
Json::Value* Json::Value::AddValue(const char* keyName, Json::Value* value)
{
if (value->name)
{
delete[] value->name;
}
value->name = CloneName(keyName);
children.push_back(value);
return value;
}
Json::Value* Json::Value::Add(Value* _parent, Value* other)
{
Value* child = new Value(_parent);
child->type = other->type;
child->name = CloneName(other->name);
switch (other->type)
{
case ValueType::Array:
case ValueType::Object:
for (auto it = other->children.begin(); it != other->children.end(); ++it)
{
child->children.push_back(Add(child, *it));
}
break;
case ValueType::Bool:
child->value.AsBool = other->value.AsBool;
break;
case ValueType::Int:
child->value.AsInt = other->value.AsInt;
break;
case ValueType::Float:
child->value.AsFloat = other->value.AsFloat;
break;
case ValueType::String:
child->value.AsString = CloneStr(other->value.AsString);
break;
}
return child;
}
Json::Value* Json::Value::Replace(Value* _parent, Value* other)
{
Value* child = nullptr;
bool newChild = false;
if (other->name != nullptr)
{
child = _parent->ByName(other->name);
}
else
{
child = _parent->ByType(other->type);
}
if (child != nullptr)
{
if (child->type != other->type)
{
child = nullptr;
}
}
if (child == nullptr)
{
child = new Value(parent);
newChild = true;
}
child->type = other->type;
if (child->name)
{
delete[] child->name;
}
child->name = CloneName(other->name);
switch (other->type)
{
case ValueType::Array:
case ValueType::Object:
for (auto it = other->children.begin(); it != other->children.end(); ++it)
{
Value* sibling = Replace(child, *it);
if (sibling != nullptr)
{
child->children.push_back(sibling);
}
}
break;
case ValueType::Bool:
child->value.AsBool = other->value.AsBool;
break;
case ValueType::Int:
child->value.AsInt = other->value.AsInt;
break;
case ValueType::Float:
child->value.AsFloat = other->value.AsFloat;
break;
case ValueType::String:
if (child->value.AsString)
{
delete[] child->value.AsString;
}
child->value.AsString = CloneStr(other->value.AsString);
break;
}
return newChild ? child : nullptr;
}
// Access
Json::Value* Json::Value::ByName(const char* byName)
{
for (auto it = children.begin(); it != children.end(); ++it)
{
Value* child = *it;
if (child->name == nullptr)
continue;
if (!strcmp(child->name, byName))
{
return child;
}
}
return nullptr;
}
Json::Value* Json::Value::ByType(const ValueType byType)
{
for (auto it = children.begin(); it != children.end(); ++it)
{
Value* child = *it;
if (child->type == byType)
{
return child;
}
}
return nullptr;
}
void Json::Serialize(void* text, size_t maxTextSize, size_t& actualTextSize)
{
actualTextSize = 0;
SerializeContext ctx = { 0 };
ctx.ptr = (uint8_t**)&text;
ctx.maxSize = maxTextSize;
ctx.actualSize = &actualTextSize;
for (auto it = root.children.begin(); it != root.children.end(); ++it)
{
(*it)->Serialize(&ctx, 0, false);
}
}
void Json::GetSerializedTextSize(void* text, size_t maxTextSize, size_t& actualTextSize)
{
actualTextSize = 0;
SerializeContext ctx = { 0 };
ctx.ptr = (uint8_t**)&text;
ctx.maxSize = maxTextSize;
ctx.actualSize = &actualTextSize;
for (auto it = root.children.begin(); it != root.children.end(); ++it)
{
(*it)->Serialize(&ctx, 0, true);
}
}
void Json::Deserialize(void* text, size_t textSize)
{
DeserializeContext ctx = { 0 };
assert(text);
ctx.ptr = (uint8_t*)text;
ctx.offset = 0;
ctx.maxSize = textSize;
root.AddObject(nullptr)->Deserialize(&ctx, nullptr);
}
// Clone
void Json::Clone(Json* other)
{
DestroyValue(&root);
for (auto it = other->root.children.begin(); it != other->root.children.end(); ++it)
{
Value* child = root.Add(&root, *it);
root.children.push_back(child);
}
}
// Merge
void Json::Merge(Json* other)
{
for (auto it = other->root.children.begin(); it != other->root.children.end(); ++it)
{
Value* child = root.Replace(&root, *it);
if (child != nullptr)
{
root.children.push_back(child);
}
}
}