#include "json.h" #include "json_p.h" #include "json_values.h" // for working with typed arrays: Alloc() and Free() #include "js_base.h" // for converting primitive types to JSON-string #include #include #include // for strtod() #include // for modf() #include namespace NSJSON { CTypedValue::CTypedValue() : m_type(vtUndefined) { } CTypedValue::CTypedValue(IBaseValue* value, ValueType type) : m_value(value), m_type(type) { } CTypedValue::~CTypedValue() { } IValue::IValue() : m_internal(new CTypedValue()) { } IValue::IValue(const std::shared_ptr& internal) : m_internal(internal) { } IValue::~IValue() { } bool IValue::IsUndefined() const { return m_internal->m_type == CTypedValue::vtUndefined; } bool IValue::IsNull() const { return m_internal->m_type == CTypedValue::vtNull; } bool IValue::IsInit() const { // the same as (m_internal->m_type != CTypedValue::vtUndefined && m_internal->m_type != CTypedValue::vtNull) but a little bit faster return m_internal->m_value.get() != nullptr; } bool IValue::IsBool() const { return (m_internal->m_type == CTypedValue::vtPrimitive && static_cast(m_internal->m_value.get())->isBool()); } bool IValue::IsInt() const { return (m_internal->m_type == CTypedValue::vtPrimitive && static_cast(m_internal->m_value.get())->isInt()); } bool IValue::IsDouble() const { return (m_internal->m_type == CTypedValue::vtPrimitive && static_cast(m_internal->m_value.get())->isDouble()); } bool IValue::IsString() const { if (m_internal->m_type != CTypedValue::vtPrimitive) return false; CPrimitive* pPrimitive = static_cast(m_internal->m_value.get()); return (pPrimitive->isStringA() || pPrimitive->isStringW()); } bool IValue::IsArray() const { return m_internal->m_type == CTypedValue::vtArray; } bool IValue::IsTypedArray() const { return m_internal->m_type == CTypedValue::vtTypedArray; } bool IValue::IsObject() const { return m_internal->m_type == CTypedValue::vtObject; } bool IValue::IsImage() const { return m_internal->m_type == CTypedValue::vtImage; } bool IValue::ToBool() const { if (m_internal->m_type != CTypedValue::vtPrimitive) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return false; } return static_cast(m_internal->m_value.get())->toBool(); } int IValue::ToInt() const { if (m_internal->m_type != CTypedValue::vtPrimitive) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return 0; } return static_cast(m_internal->m_value.get())->toInt(); } double IValue::ToDouble() const { if (m_internal->m_type != CTypedValue::vtPrimitive) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return 0.0; } return static_cast(m_internal->m_value.get())->toDouble(); } std::string IValue::ToStringA() const { if (m_internal->m_type != CTypedValue::vtPrimitive) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return ""; } return static_cast(m_internal->m_value.get())->toStringA(); } std::wstring IValue::ToStringW() const { if (m_internal->m_type != CTypedValue::vtPrimitive) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return L""; } return static_cast(m_internal->m_value.get())->toStringW(); } IValue::operator bool() const { return ToBool(); } IValue::operator int() const { return ToInt(); } IValue::operator double() const { return ToDouble(); } IValue::operator std::string() const { return ToStringA(); } IValue::operator std::wstring() const { return ToStringW(); } namespace { std::string doubleToJsonString(double value) { // handle special cases if (std::isnan(value) || std::isinf(value)) return "null"; // convert to string with full precision std::ostringstream oss; oss.precision(std::numeric_limits::digits10); oss << std::noshowpoint << value; return oss.str(); } std::string stringToJsonString(const std::string& value) { std::string result; for (char ch : value) { if (ch <= 0x1F) { result += '\\'; if (ch == '\b') { result += 'b'; } else if (ch == '\t') { result += 't'; } else if (ch == '\n') { result += 'n'; } else if (ch == '\f') { result += 'f'; } else if (ch == '\r') { result += 'r'; } else { result += "u00"; std::ostringstream oss; oss << std::setfill('0') << std::setw(2) << std::hex << (int)ch; result += oss.str(); } } else if (ch == '\\') { result += "\\\\"; } else if (ch == '\"') { result += "\\\""; } else { result += ch; } } return result; } } std::string IValue::ToJSON() { std::string strRes; CTypedValue::ValueType type = m_internal->m_type; switch (type) { case CTypedValue::vtUndefined: { break; } case CTypedValue::vtNull: { strRes = "null"; break; } case CTypedValue::vtPrimitive: { CPrimitive* pPrimitiveValue = static_cast(m_internal->m_value.get()); if (pPrimitiveValue->isBool()) { strRes = pPrimitiveValue->toBool() ? "true" : "false"; } else if (pPrimitiveValue->isInt()) { strRes = std::to_string(pPrimitiveValue->toInt()); } else if (pPrimitiveValue->isDouble()) { strRes = doubleToJsonString(pPrimitiveValue->toDouble()); } else { strRes = "\"" + stringToJsonString(pPrimitiveValue->toStringA()) + "\""; } break; } case CTypedValue::vtArray: { CArray* pArrayValue = static_cast(m_internal->m_value.get()); strRes = "["; const int len = pArrayValue->getCount(); for (int i = 0; i < len; i++) { CValue& value = pArrayValue->get(i); if (value.IsUndefined()) strRes += "null"; else strRes += value.ToJSON(); strRes += ','; } // remove last ',' if (strRes.back() == ',') strRes.pop_back(); strRes += "]"; break; } case CTypedValue::vtTypedArray: { CTypedArray* pTypedArrayValue = static_cast(m_internal->m_value.get()); strRes += "{"; const int size = pTypedArrayValue->getCount(); BYTE* data = pTypedArrayValue->getData(); for (int i = 0; i < size; i++) { strRes += "\"" + std::to_string(i) + "\":"; strRes += std::to_string((int)data[i]); strRes += ','; } // remove last ',' if (strRes.back() == ',') strRes.pop_back(); strRes += "}"; break; } case CTypedValue::vtObject: { CObject* pObjectValue = static_cast(m_internal->m_value.get()); strRes += "{"; std::vector propertyNames = pObjectValue->getPropertyNames(); const int count = propertyNames.size(); for (int i = 0; i < count; i++) { CValue& value = pObjectValue->get(propertyNames[i]); if (value.IsUndefined()) continue; strRes += "\"" + propertyNames[i] + "\":"; strRes += value.ToJSON(); strRes += ','; } // remove last ',' if (strRes.back() == ',') strRes.pop_back(); strRes += "}"; break; } case CTypedValue::vtImage: { // TODO: implement like typed array? break; } } return strRes; } IValue::IValue(bool value) : m_internal(new CTypedValue(new CPrimitive(value), CTypedValue::vtPrimitive)) { } IValue::IValue(int value) : m_internal(new CTypedValue(new CPrimitive(value), CTypedValue::vtPrimitive)) { } IValue::IValue(double value) : m_internal(new CTypedValue(new CPrimitive(value), CTypedValue::vtPrimitive)) { } IValue::IValue(const char* value) : m_internal(new CTypedValue(new CPrimitive(std::string(value)), CTypedValue::vtPrimitive)) { } IValue::IValue(const std::string& value) : m_internal(new CTypedValue(new CPrimitive(value), CTypedValue::vtPrimitive)) { } IValue::IValue(const wchar_t* value) : m_internal(new CTypedValue(new CPrimitive(std::wstring(value)), CTypedValue::vtPrimitive)) { } IValue::IValue(const std::wstring& value) : m_internal(new CTypedValue(new CPrimitive(value), CTypedValue::vtPrimitive)) { } int IValue::GetCount() const { if (m_internal->m_type == CTypedValue::vtArray) { return static_cast(m_internal->m_value.get())->getCount(); } else if (m_internal->m_type == CTypedValue::vtTypedArray) { return static_cast(m_internal->m_value.get())->getCount(); } else { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return 0; } } const CValueRef IValue::Get(int index) const { if (m_internal->m_type != CTypedValue::vtArray) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return CValue(); } if (index < 0 || index >= GetCount()) { #ifdef JSON_DEBUG throw std::out_of_range("std::out_of_range"); #endif return CValue(); } return static_cast(m_internal->m_value.get())->get(index); } CValueRef IValue::Get(int index) { return static_cast(*this).Get(index); } const CValueRef IValue::operator[](int index) const { return Get(index); } CValueRef IValue::operator[](int index) { return Get(index); } IValue::IValue(std::initializer_list elements) : m_internal(new CTypedValue(new CArray(elements), CTypedValue::vtArray)) { } const BYTE* IValue::GetData() const { if (m_internal->m_type != CTypedValue::vtTypedArray) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return nullptr; } return static_cast(m_internal->m_value.get())->getData(); } BYTE* IValue::GetData() { return const_cast(static_cast(*this).GetData()); } const CValueRef IValue::Get(const char* name) const { if (m_internal->m_type != CTypedValue::vtObject) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return CValue(); } return static_cast(m_internal->m_value.get())->get(name); } CValueRef IValue::Get(const char* name) { return static_cast(*this).Get(name); } const CValueRef IValue::operator[](const char* name) const { return Get(name); } CValueRef IValue::operator[](const char* name) { return Get(name); } std::vector IValue::GetPropertyNames() const { if (m_internal->m_type != CTypedValue::vtObject) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return {}; } return static_cast(m_internal->m_value.get())->getPropertyNames(); } const BYTE* IValue::GetImageBits() const { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return nullptr; } return static_cast(m_internal->m_value.get())->getBits(); } BYTE* IValue::GetImageBits() { return const_cast(static_cast(*this).GetImageBits()); } int IValue::GetImageWidth() const { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return 0; } return static_cast(m_internal->m_value.get())->getWidth(); } int IValue::GetImageHeight() const { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return 0; } return static_cast(m_internal->m_value.get())->getHeight(); } ImageFormat IValue::GetImageFormat() const { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return ImageFormat::ifInvalid; } return static_cast(m_internal->m_value.get())->getFormat(); } void IValue::ImageExternalize() { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return; } static_cast(m_internal->m_value.get())->externalize(); } void IValue::ImageAlloc(const int& width, const int& height, const ImageFormat& format) { if (m_internal->m_type != CTypedValue::vtImage) { #ifdef JSON_DEBUG throw std::bad_cast(); #endif return; } static_cast(m_internal->m_value.get())->alloc(width, height, format); } CValue::CValue() : IValue() { } CValue::CValue(const CValue& other) : IValue(std::make_shared(*other.m_internal)) { } CValue::CValue(const CValueRef& ref) : IValue(std::make_shared(*ref.m_internal)) { } CValue::~CValue() { } CValue& CValue::operator=(const CValue& other) { *m_internal = *other.m_internal; return *this; } CValue& CValue::operator=(const CValueRef& ref) { *m_internal = *ref.m_internal; return *this; } CValue::CValue(bool value) : IValue(value) { } CValue::CValue(int value) : IValue(value) { } CValue::CValue(double value) : IValue(value) { } CValue::CValue(const char* value) : IValue(value) { } CValue::CValue(const std::string& value) : IValue(value) { } CValue::CValue(const wchar_t* value) : IValue(value) { } CValue::CValue(const std::wstring& value) : IValue(value) { } CValue::CValue(std::initializer_list elements) : IValue(elements) { } CValue CValue::CreateArray(int count) { CValue ret; if (count < 0) return ret; ret.m_internal->m_value = std::make_shared(count); ret.m_internal->m_type = CTypedValue::vtArray; return ret; } CValue CValue::CreateTypedArray(BYTE* data, int count, bool isExternalize) { CValue ret; if (count <= 0) return ret; ret.m_internal->m_value = std::make_shared(data, count, isExternalize); ret.m_internal->m_type = CTypedValue::vtTypedArray; return ret; } BYTE* CValue::AllocTypedArray(size_t size) { return NSJSBase::NSAllocator::Alloc(size); } void CValue::FreeTypedArray(BYTE* data, size_t size) { NSJSBase::NSAllocator::Free(data, size); } CValue CValue::CreateImage(BYTE* bits, int width, int height, ImageFormat format, bool isExternalize) { CValue ret; if (width <= 0 || height <= 0) return ret; ret.m_internal->m_value = std::make_shared(bits, width, height, format, isExternalize); ret.m_internal->m_type = CTypedValue::vtImage; return ret; } CValue CValue::CreateEmptyImage(ImageFormat format) { CValue ret; ret.m_internal->m_value = std::make_shared((BYTE*)NULL, 0, 0, format, false); ret.m_internal->m_type = CTypedValue::vtImage; return ret; } BYTE* CValue::AllocImageBits(int width, int height) { return new BYTE[4 * width * height]; } void CValue::FreeImageBits(BYTE* bits) { delete[] bits; } CValue CValue::CreateObject() { CValue ret; ret.m_internal->m_value = std::make_shared(); ret.m_internal->m_type = CTypedValue::vtObject; return ret; } CValue CValue::CreateUndefined() { return CValue(); } CValue CValue::CreateNull() { CValue ret; ret.m_internal->m_type = CTypedValue::vtNull; return ret; } namespace { // moves pos to first non-space symbol and returns false if end of the string was reached bool skipWhitespaces(const std::string& str, int& pos) { for (; pos < str.size(); pos++) { char ch = str[pos]; if (ch != '\t' && ch != '\n' && ch != '\r' && ch != ' ') break; } return pos < str.size(); } CValue parseNumberFromJSON(const std::string& str, int& pos) { const char* startPos = str.c_str() + pos; char* newPos; double number = std::strtod(startPos, &newPos); if (newPos == startPos) return CValue(); CValue value; if (std::isfinite(number)) { // check if number can be represented as an integer type without loss of precision double integral; // integral part double fractional = std::modf(number, &integral); // fractional part if (fractional == 0.0 && integral >= std::numeric_limits::min() && integral <= std::numeric_limits::max()) value = (int)integral; else value = number; } else { value = number; } pos += newPos - startPos; return value; } CValue parseStringFromJSON(const std::string& str, int& pos) { // skip '\"' cause it has already been checked pos++; std::string result; while (pos < str.size() && str[pos] != '\"') { // any non-escaped control characters are not allowed if (str[pos] < 0x20) return CValue(); if (str[pos] != '\\') { result += str[pos++]; } else { pos++; if (pos >= str.size()) return CValue(); char ch = str[pos++]; if (ch == '\\' || ch == '\"' || ch == '/') { result += ch; } else if (ch == 'b') { result += '\b'; } else if (ch == 't') { result += '\t'; } else if (ch == 'n') { result += '\n'; } else if (ch == 'f') { result += '\f'; } else if (ch == 'r') { result += '\r'; } else if (ch == 'u') { if (str.size() - pos < 4) return CValue(); // TODO: support unicode symbols with codes > U+0020 if (str[pos] != '0' || str[pos + 1] != '0') return CValue(); pos += 2; std::string strHex = str.substr(pos, 2); pos += 2; // std::stoi throws std::invalid_argument if string is invalid try { std::size_t count; char symbol = (char)std::stoi(strHex, &count, 16); if (count < 2) return CValue(); result += symbol; } catch (std::invalid_argument& e) { return CValue(); } } else { return CValue(); } } } // if did not encounter closing qoutes, return undefined if (pos == str.size()) return CValue(); pos++; return result; } // parse one value of JSON-string `str` starting from `pos` CValue valueFromJSON(const std::string& str, int& pos); CValue parseArrayFromJSON(const std::string& str, int& pos) { // skip opening bracket '[' cause it has already been checked pos++; // handle first element separately to get pattern: [(firstValue)(,value)(,value)...] CValue firstValue = valueFromJSON(str, pos); if (firstValue.IsUndefined()) { if (pos < str.size() && str[pos] == ']') { pos++; return CValue::CreateArray(0); } else { return CValue(); } } std::vector values; values.emplace_back(firstValue); while (skipWhitespaces(str, pos) && str[pos] != ']') { // expect ',' if (str[pos] != ',') return CValue(); pos++; // expect value CValue value = valueFromJSON(str, pos); if (value.IsUndefined()) return CValue(); values.emplace_back(value); } // if did not encounter closing bracket ']', return undefined if (pos == str.size()) return CValue(); pos++; // copy values from vector to CValue array CValue result = CValue::CreateArray(values.size()); for (int i = 0; i < values.size(); i++) { result[i] = values[i]; } return result; } std::pair parseKeyValuePair(const std::string& str, int& pos) { std::pair result; if (!skipWhitespaces(str, pos) || str[pos] != '\"') return result; // parse key CValue key = parseStringFromJSON(str, pos); if (key.IsUndefined()) return result; result.first = key.ToStringA(); // expect ':' if (!skipWhitespaces(str, pos) || str[pos] != ':') return result; pos++; // parse value CValue value = valueFromJSON(str, pos); if (value.IsUndefined()) return result; result.second = value; return result; } CValue parseObjectFromJSON(const std::string& str, int& pos) { CValue result = CValue::CreateObject(); // skip opening curly brace '{' cause it has already been checked pos++; // handle first key-value pair separately to get pattern: [(firstKey:firstValue)(,key:value)(,key:value)...] std::pair keyValue = parseKeyValuePair(str, pos); // if value is undefined, then something went wrong or object is empty if (keyValue.second.IsUndefined()) { if (pos < str.size() && str[pos] == '}' && keyValue.first.empty()) { pos++; return result; } else { return CValue(); } } result[keyValue.first.c_str()] = keyValue.second; while (skipWhitespaces(str, pos) && str[pos] != '}') { // expect ',' if (str[pos] != ',') return CValue(); pos++; // expect key-value pair std::pair keyValue = parseKeyValuePair(str, pos); if (keyValue.second.IsUndefined()) return CValue(); result[keyValue.first.c_str()] = keyValue.second; } // if did not encounter closing curly brace '}', return undefined if (pos == str.size()) return CValue(); pos++; return result; } CValue valueFromJSON(const std::string& str, int& pos) { if (!skipWhitespaces(str, pos)) return CValue(); CValue value; char ch = str[pos]; if (ch == '\"') { // string value = parseStringFromJSON(str, pos); } else if (ch == '[') { // array value = parseArrayFromJSON(str, pos); } else if (ch == '{') { // object value = parseObjectFromJSON(str, pos); } else if (ch == 'n') { // null if (str.substr(pos, 4) == "null") value = CValue::CreateNull(); else return CValue(); pos += 4; } else if (ch == 't') { // true (bool) if (str.substr(pos, 4) == "true") value = true; else return CValue(); pos += 4; } else if (ch == 'f') { // false (bool) if (str.substr(pos, 5) == "false") value = false; else return CValue(); pos += 5; } else if (ch == '-' || (ch >= '0' && ch <= '9')) { value = parseNumberFromJSON(str, pos); } else { return CValue(); } return value; } } CValue CValue::FromJSON(const std::string& jsonString) { int pos = 0; CValue value = valueFromJSON(jsonString, pos); // if there are still some non-space characters after the value has been parsed, // the JSON-string is considered invalid if (skipWhitespaces(jsonString, pos)) return CValue(); return value; } CValueRef::CValueRef(const CValueRef& other) : IValue(other.m_internal) { } CValueRef::CValueRef(const CValue& value) : IValue(value.m_internal) { } CValueRef::~CValueRef() { } CValueRef& CValueRef::operator=(const CValueRef& other) { // not reassign the pointer, but copy its content! *m_internal = *other.m_internal; return *this; } CValueRef& CValueRef::operator=(const CValue& value) { // not reassign the pointer, but copy its content! *m_internal = *value.m_internal; return *this; } }