#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); } } }