Refactor
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "RuntimeJson.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -10,6 +11,59 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
int HexDigitValue(char ch)
|
||||
{
|
||||
if (ch >= '0' && ch <= '9')
|
||||
return ch - '0';
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
return ch - 'a' + 10;
|
||||
if (ch >= 'A' && ch <= 'F')
|
||||
return ch - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsHighSurrogate(unsigned int codePoint)
|
||||
{
|
||||
return codePoint >= 0xD800 && codePoint <= 0xDBFF;
|
||||
}
|
||||
|
||||
bool IsLowSurrogate(unsigned int codePoint)
|
||||
{
|
||||
return codePoint >= 0xDC00 && codePoint <= 0xDFFF;
|
||||
}
|
||||
|
||||
void AppendUtf8(unsigned int codePoint, std::ostringstream& output)
|
||||
{
|
||||
if (codePoint <= 0x7F)
|
||||
{
|
||||
output << static_cast<char>(codePoint);
|
||||
}
|
||||
else if (codePoint <= 0x7FF)
|
||||
{
|
||||
output << static_cast<char>(0xC0 | ((codePoint >> 6) & 0x1F));
|
||||
output << static_cast<char>(0x80 | (codePoint & 0x3F));
|
||||
}
|
||||
else if (codePoint <= 0xFFFF)
|
||||
{
|
||||
output << static_cast<char>(0xE0 | ((codePoint >> 12) & 0x0F));
|
||||
output << static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F));
|
||||
output << static_cast<char>(0x80 | (codePoint & 0x3F));
|
||||
}
|
||||
else
|
||||
{
|
||||
output << static_cast<char>(0xF0 | ((codePoint >> 18) & 0x07));
|
||||
output << static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F));
|
||||
output << static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F));
|
||||
output << static_cast<char>(0x80 | (codePoint & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
void AppendControlEscape(unsigned char ch, std::ostringstream& output)
|
||||
{
|
||||
const char* digits = "0123456789ABCDEF";
|
||||
output << "\\u00" << digits[(ch >> 4) & 0x0F] << digits[ch & 0x0F];
|
||||
}
|
||||
|
||||
class JsonParser
|
||||
{
|
||||
public:
|
||||
@@ -181,8 +235,9 @@ private:
|
||||
case 'r': result << '\r'; break;
|
||||
case 't': result << '\t'; break;
|
||||
case 'u':
|
||||
setError("Unicode escape sequences are not supported in this JSON parser.");
|
||||
return false;
|
||||
if (!parseUnicodeEscape(result))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
setError("Invalid escape sequence in JSON string.");
|
||||
return false;
|
||||
@@ -190,6 +245,11 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
if (static_cast<unsigned char>(ch) < 0x20)
|
||||
{
|
||||
setError("Unescaped control character in JSON string.");
|
||||
return false;
|
||||
}
|
||||
result << ch;
|
||||
}
|
||||
}
|
||||
@@ -198,6 +258,66 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parseHexCodePoint(unsigned int& codePoint)
|
||||
{
|
||||
if (mPosition + 4 > mText.size())
|
||||
{
|
||||
setError("Unexpected end of Unicode escape sequence.");
|
||||
return false;
|
||||
}
|
||||
|
||||
codePoint = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
const int digit = HexDigitValue(mText[mPosition + i]);
|
||||
if (digit < 0)
|
||||
{
|
||||
setError("Invalid Unicode escape sequence in JSON string.");
|
||||
return false;
|
||||
}
|
||||
codePoint = (codePoint << 4) | static_cast<unsigned int>(digit);
|
||||
}
|
||||
|
||||
mPosition += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseUnicodeEscape(std::ostringstream& result)
|
||||
{
|
||||
unsigned int codePoint = 0;
|
||||
if (!parseHexCodePoint(codePoint))
|
||||
return false;
|
||||
|
||||
if (IsHighSurrogate(codePoint))
|
||||
{
|
||||
if (mPosition + 2 > mText.size() || mText[mPosition] != '\\' || mText[mPosition + 1] != 'u')
|
||||
{
|
||||
setError("High surrogate Unicode escape must be followed by a low surrogate.");
|
||||
return false;
|
||||
}
|
||||
|
||||
mPosition += 2;
|
||||
unsigned int lowSurrogate = 0;
|
||||
if (!parseHexCodePoint(lowSurrogate))
|
||||
return false;
|
||||
if (!IsLowSurrogate(lowSurrogate))
|
||||
{
|
||||
setError("High surrogate Unicode escape must be followed by a low surrogate.");
|
||||
return false;
|
||||
}
|
||||
|
||||
codePoint = 0x10000 + (((codePoint - 0xD800) << 10) | (lowSurrogate - 0xDC00));
|
||||
}
|
||||
else if (IsLowSurrogate(codePoint))
|
||||
{
|
||||
setError("Low surrogate Unicode escape without preceding high surrogate.");
|
||||
return false;
|
||||
}
|
||||
|
||||
AppendUtf8(codePoint, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseNumber(JsonValue& value)
|
||||
{
|
||||
std::size_t start = mPosition;
|
||||
@@ -205,12 +325,40 @@ private:
|
||||
if (mText[mPosition] == '-')
|
||||
++mPosition;
|
||||
|
||||
while (mPosition < mText.size() && std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
if (mPosition >= mText.size())
|
||||
{
|
||||
setError("Invalid JSON number.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mText[mPosition] == '0')
|
||||
{
|
||||
++mPosition;
|
||||
if (mPosition < mText.size() && std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
{
|
||||
setError("JSON numbers must not contain leading zeroes.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (mText[mPosition] >= '1' && mText[mPosition] <= '9')
|
||||
{
|
||||
while (mPosition < mText.size() && std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
++mPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
setError("Invalid JSON number.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mPosition < mText.size() && mText[mPosition] == '.')
|
||||
{
|
||||
++mPosition;
|
||||
if (mPosition >= mText.size() || !std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
{
|
||||
setError("JSON number fraction must contain at least one digit.");
|
||||
return false;
|
||||
}
|
||||
while (mPosition < mText.size() && std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
++mPosition;
|
||||
}
|
||||
@@ -220,14 +368,20 @@ private:
|
||||
++mPosition;
|
||||
if (mPosition < mText.size() && (mText[mPosition] == '+' || mText[mPosition] == '-'))
|
||||
++mPosition;
|
||||
if (mPosition >= mText.size() || !std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
{
|
||||
setError("JSON number exponent must contain at least one digit.");
|
||||
return false;
|
||||
}
|
||||
while (mPosition < mText.size() && std::isdigit(static_cast<unsigned char>(mText[mPosition])))
|
||||
++mPosition;
|
||||
}
|
||||
|
||||
std::string token = mText.substr(start, mPosition - start);
|
||||
char* endPtr = nullptr;
|
||||
errno = 0;
|
||||
double parsed = strtod(token.c_str(), &endPtr);
|
||||
if (endPtr == token.c_str() || *endPtr != '\0')
|
||||
if (endPtr == token.c_str() || *endPtr != '\0' || errno == ERANGE || !std::isfinite(parsed))
|
||||
{
|
||||
setError("Invalid JSON number.");
|
||||
return false;
|
||||
@@ -322,7 +476,12 @@ void SerializeJsonImpl(const JsonValue& value, std::ostringstream& output, bool
|
||||
case '\n': output << "\\n"; break;
|
||||
case '\r': output << "\\r"; break;
|
||||
case '\t': output << "\\t"; break;
|
||||
default: output << ch; break;
|
||||
default:
|
||||
if (static_cast<unsigned char>(ch) < 0x20)
|
||||
AppendControlEscape(static_cast<unsigned char>(ch), output);
|
||||
else
|
||||
output << ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
output << '"';
|
||||
@@ -407,14 +566,14 @@ JsonValue::JsonValue(const std::string& value)
|
||||
JsonValue JsonValue::MakeArray()
|
||||
{
|
||||
JsonValue value;
|
||||
value.mType = Type::Array;
|
||||
value.reset(Type::Array);
|
||||
return value;
|
||||
}
|
||||
|
||||
JsonValue JsonValue::MakeObject()
|
||||
{
|
||||
JsonValue value;
|
||||
value.mType = Type::Object;
|
||||
value.reset(Type::Object);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -449,20 +608,14 @@ const std::map<std::string, JsonValue>& JsonValue::asObject() const
|
||||
std::vector<JsonValue>& JsonValue::array()
|
||||
{
|
||||
if (mType != Type::Array)
|
||||
{
|
||||
mType = Type::Array;
|
||||
mArrayValue.clear();
|
||||
}
|
||||
reset(Type::Array);
|
||||
return mArrayValue;
|
||||
}
|
||||
|
||||
std::map<std::string, JsonValue>& JsonValue::object()
|
||||
{
|
||||
if (mType != Type::Object)
|
||||
{
|
||||
mType = Type::Object;
|
||||
mObjectValue.clear();
|
||||
}
|
||||
reset(Type::Object);
|
||||
return mObjectValue;
|
||||
}
|
||||
|
||||
@@ -485,6 +638,16 @@ const JsonValue* JsonValue::find(const std::string& key) const
|
||||
return iterator != mObjectValue.end() ? &iterator->second : nullptr;
|
||||
}
|
||||
|
||||
void JsonValue::reset(Type type)
|
||||
{
|
||||
mType = type;
|
||||
mBooleanValue = false;
|
||||
mNumberValue = 0.0;
|
||||
mStringValue.clear();
|
||||
mArrayValue.clear();
|
||||
mObjectValue.clear();
|
||||
}
|
||||
|
||||
bool ParseJson(const std::string& text, JsonValue& value, std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
|
||||
Reference in New Issue
Block a user