Merge pull request #1 from Caldfir/read_hex
allowHexadecimal reader option
This commit is contained in:
commit
157b166e23
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@
|
|||||||
/doc/doxyfile
|
/doc/doxyfile
|
||||||
/dist/
|
/dist/
|
||||||
/.cache/
|
/.cache/
|
||||||
|
/.vscode/
|
||||||
|
|
||||||
# MSVC project files:
|
# MSVC project files:
|
||||||
*.sln
|
*.sln
|
||||||
|
@ -72,7 +72,7 @@ project(jsoncpp
|
|||||||
# 2. ./include/json/version.h
|
# 2. ./include/json/version.h
|
||||||
# 3. ./CMakeLists.txt
|
# 3. ./CMakeLists.txt
|
||||||
# IMPORTANT: also update the PROJECT_SOVERSION!!
|
# IMPORTANT: also update the PROJECT_SOVERSION!!
|
||||||
VERSION 1.9.5 # <major>[.<minor>[.<patch>[.<tweak>]]]
|
VERSION 1.10.0 # <major>[.<minor>[.<patch>[.<tweak>]]]
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||||
|
@ -324,6 +324,8 @@ public:
|
|||||||
* - `"allowSpecialFloats": false or true`
|
* - `"allowSpecialFloats": false or true`
|
||||||
* - If true, special float values (NaNs and infinities) are allowed and
|
* - If true, special float values (NaNs and infinities) are allowed and
|
||||||
* their values are lossfree restorable.
|
* their values are lossfree restorable.
|
||||||
|
* - `"allowHexadecimal": false or true`
|
||||||
|
* - If true, allow hexadecimal (eg 0xFFFF) to be used as unsigned integers.
|
||||||
* - `"skipBom": false or true`
|
* - `"skipBom": false or true`
|
||||||
* - If true, if the input starts with the Unicode byte order mark (BOM),
|
* - If true, if the input starts with the Unicode byte order mark (BOM),
|
||||||
* it is skipped.
|
* it is skipped.
|
||||||
|
@ -875,6 +875,7 @@ public:
|
|||||||
bool failIfExtra_;
|
bool failIfExtra_;
|
||||||
bool rejectDupKeys_;
|
bool rejectDupKeys_;
|
||||||
bool allowSpecialFloats_;
|
bool allowSpecialFloats_;
|
||||||
|
bool allowHexadecimal_;
|
||||||
bool skipBom_;
|
bool skipBom_;
|
||||||
size_t stackLimit_;
|
size_t stackLimit_;
|
||||||
}; // OurFeatures
|
}; // OurFeatures
|
||||||
@ -914,6 +915,7 @@ private:
|
|||||||
tokenArrayEnd,
|
tokenArrayEnd,
|
||||||
tokenString,
|
tokenString,
|
||||||
tokenNumber,
|
tokenNumber,
|
||||||
|
tokenHexadecimal,
|
||||||
tokenTrue,
|
tokenTrue,
|
||||||
tokenFalse,
|
tokenFalse,
|
||||||
tokenNull,
|
tokenNull,
|
||||||
@ -952,11 +954,14 @@ private:
|
|||||||
bool readString();
|
bool readString();
|
||||||
bool readStringSingleQuote();
|
bool readStringSingleQuote();
|
||||||
bool readNumber(bool checkInf);
|
bool readNumber(bool checkInf);
|
||||||
|
bool readHexadecimal();
|
||||||
bool readValue();
|
bool readValue();
|
||||||
bool readObject(Token& token);
|
bool readObject(Token& token);
|
||||||
bool readArray(Token& token);
|
bool readArray(Token& token);
|
||||||
bool decodeNumber(Token& token);
|
bool decodeNumber(Token& token);
|
||||||
bool decodeNumber(Token& token, Value& decoded);
|
bool decodeNumber(Token& token, Value& decoded);
|
||||||
|
bool decodeHexadecimal(Token& token);
|
||||||
|
bool decodeHexadecimal(Token& token, Value& decoded);
|
||||||
bool decodeString(Token& token);
|
bool decodeString(Token& token);
|
||||||
bool decodeString(Token& token, String& decoded);
|
bool decodeString(Token& token, String& decoded);
|
||||||
bool decodeDouble(Token& token);
|
bool decodeDouble(Token& token);
|
||||||
@ -1078,6 +1083,9 @@ bool OurReader::readValue() {
|
|||||||
case tokenNumber:
|
case tokenNumber:
|
||||||
successful = decodeNumber(token);
|
successful = decodeNumber(token);
|
||||||
break;
|
break;
|
||||||
|
case tokenHexadecimal:
|
||||||
|
successful = decodeHexadecimal(token);
|
||||||
|
break;
|
||||||
case tokenString:
|
case tokenString:
|
||||||
successful = decodeString(token);
|
successful = decodeString(token);
|
||||||
break;
|
break;
|
||||||
@ -1191,6 +1199,16 @@ bool OurReader::readToken(Token& token) {
|
|||||||
ok = readComment();
|
ok = readComment();
|
||||||
break;
|
break;
|
||||||
case '0':
|
case '0':
|
||||||
|
if(match("x", 1)) {
|
||||||
|
token.type_ = tokenHexadecimal;
|
||||||
|
ok = features_.allowHexadecimal_;
|
||||||
|
readHexadecimal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
token.type_ = tokenNumber;
|
||||||
|
readNumber(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '1':
|
case '1':
|
||||||
case '2':
|
case '2':
|
||||||
case '3':
|
case '3':
|
||||||
@ -1419,6 +1437,20 @@ bool OurReader::readNumber(bool checkInf) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OurReader::readHexadecimal(void) {
|
||||||
|
Location p = current_;
|
||||||
|
for (; p < end_; ++p)
|
||||||
|
{
|
||||||
|
char c = *p;
|
||||||
|
if ( (c < '0' || c > '9')
|
||||||
|
&& (c < 'a' || c > 'f')
|
||||||
|
&& (c < 'A' || c > 'F') ) break;
|
||||||
|
}
|
||||||
|
current_ = p;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool OurReader::readString() {
|
bool OurReader::readString() {
|
||||||
Char c = 0;
|
Char c = 0;
|
||||||
while (current_ != end_) {
|
while (current_ != end_) {
|
||||||
@ -1639,6 +1671,41 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OurReader::decodeHexadecimal(Token& token) {
|
||||||
|
Value decoded;
|
||||||
|
if (!decodeHexadecimal(token, decoded))
|
||||||
|
return false;
|
||||||
|
currentValue().swapPayload(decoded);
|
||||||
|
currentValue().setOffsetStart(token.start_ - begin_);
|
||||||
|
currentValue().setOffsetLimit(token.end_ - begin_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OurReader::decodeHexadecimal(Token& token, Value& decoded) {
|
||||||
|
Location current = token.start_;
|
||||||
|
if (current < token.end_ && *current == '0') ++current;
|
||||||
|
if (current < token.end_ && *current == 'x') ++current;
|
||||||
|
Json::LargestUInt value = 0;
|
||||||
|
if (current >= token.end_)
|
||||||
|
return addError("Zero hexadecimal digits.", token);
|
||||||
|
if (current + (sizeof(value) * 2) < token.end_)
|
||||||
|
return addError("Token too long to be unsigned integer.", token, current);
|
||||||
|
for (; current < token.end_; ++current) {
|
||||||
|
Char c = *current;
|
||||||
|
if (c >= 'a')
|
||||||
|
c -= 'a' - 10;
|
||||||
|
else if (c >= 'A')
|
||||||
|
c -= 'A' - 10;
|
||||||
|
else if (c >= '0')
|
||||||
|
c -= '0';
|
||||||
|
else
|
||||||
|
return addError("Contains non-hexadecimal digits.", token, current);
|
||||||
|
value = value << 4 | static_cast<Value::UInt>(c);
|
||||||
|
}
|
||||||
|
decoded = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool OurReader::decodeDouble(Token& token) {
|
bool OurReader::decodeDouble(Token& token) {
|
||||||
Value decoded;
|
Value decoded;
|
||||||
if (!decodeDouble(token, decoded))
|
if (!decodeDouble(token, decoded))
|
||||||
@ -1908,6 +1975,7 @@ CharReader* CharReaderBuilder::newCharReader() const {
|
|||||||
features.failIfExtra_ = settings_["failIfExtra"].asBool();
|
features.failIfExtra_ = settings_["failIfExtra"].asBool();
|
||||||
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
|
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
|
||||||
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
|
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
|
||||||
|
features.allowHexadecimal_ = settings_["allowHexadecimal"].asBool();
|
||||||
features.skipBom_ = settings_["skipBom"].asBool();
|
features.skipBom_ = settings_["skipBom"].asBool();
|
||||||
return new OurCharReader(collectComments, features);
|
return new OurCharReader(collectComments, features);
|
||||||
}
|
}
|
||||||
@ -1925,6 +1993,7 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const {
|
|||||||
"failIfExtra",
|
"failIfExtra",
|
||||||
"rejectDupKeys",
|
"rejectDupKeys",
|
||||||
"allowSpecialFloats",
|
"allowSpecialFloats",
|
||||||
|
"allowHexadecimal",
|
||||||
"skipBom",
|
"skipBom",
|
||||||
};
|
};
|
||||||
for (auto si = settings_.begin(); si != settings_.end(); ++si) {
|
for (auto si = settings_.begin(); si != settings_.end(); ++si) {
|
||||||
|
@ -3556,6 +3556,98 @@ JSONTEST_FIXTURE_LOCAL(CharReaderAllowZeroesTest, issue176) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CharReaderAllowHexadecimal : JsonTest::TestCase {};
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, disallowHex) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
CharReaderPtr reader(b.newCharReader());
|
||||||
|
Json::Value root;
|
||||||
|
Json::String errs;
|
||||||
|
{
|
||||||
|
char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })";
|
||||||
|
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
|
||||||
|
JSONTEST_ASSERT(!ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(
|
||||||
|
"* Line 1, Column 7\n"
|
||||||
|
" Syntax error: value, object or array expected.\n",
|
||||||
|
errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexObject) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
b.settings_["allowHexadecimal"] = true;
|
||||||
|
Json::Value invalid;
|
||||||
|
JSONTEST_ASSERT(b.validate(&invalid)) << invalid;
|
||||||
|
CharReaderPtr reader(b.newCharReader());
|
||||||
|
{
|
||||||
|
Json::Value root;
|
||||||
|
Json::String errs;
|
||||||
|
char const doc[] = R"({ "a":0x9, "b":0xf, "c":0xF })";
|
||||||
|
bool ok = reader->parse(doc, doc + std::strlen(doc), &root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL(3u, root.size());
|
||||||
|
JSONTEST_ASSERT_EQUAL(0x9u, root.get("a", 0).asUInt());
|
||||||
|
JSONTEST_ASSERT_EQUAL(0xfu, root.get("b", 0).asUInt());
|
||||||
|
JSONTEST_ASSERT_EQUAL(0xFu, root.get("c", 0).asUInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE_LOCAL(CharReaderAllowHexadecimal, hexNumbers) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
b.settings_["allowHexadecimal"] = true;
|
||||||
|
CharReaderPtr reader(b.newCharReader());
|
||||||
|
|
||||||
|
struct TestData {
|
||||||
|
bool ok;
|
||||||
|
Json::LargestUInt out;
|
||||||
|
Json::String in;
|
||||||
|
};
|
||||||
|
constexpr int _ = 0; // ignored
|
||||||
|
const TestData test_data[] = {
|
||||||
|
{true, 99, "99"}, // regular number
|
||||||
|
{true, 0x99, "0x99"}, // hexadecimal number
|
||||||
|
{false, _, "AA"}, // missing prefix
|
||||||
|
{false, _, "xAA"}, // partial prefix
|
||||||
|
{true, 0xAA, "0xAA"}, // with prefix
|
||||||
|
{true, 0x00, "0x00"}, // zero
|
||||||
|
{true, 0x0123456789, "0x0123456789"}, // numeric hex
|
||||||
|
{true, 0xABCDEF, "0xABCDEF"}, // uppercase-letter hex
|
||||||
|
{true, 0xabcdef, "0xabcdef"}, // lowercase-letter hex
|
||||||
|
#ifdef JSON_HAS_INT64
|
||||||
|
{true, 0xFfffFfffFfffFfff, "0xFfffFfffFfffFfff"}, // max
|
||||||
|
{false, _, "0x1FfffFfffFfffFfff"}, // too long
|
||||||
|
#else
|
||||||
|
{true, 0xFfffFfff, "0xFfffFfff"}, // max
|
||||||
|
{false, _, "0x1FfffFfff"}, // too long
|
||||||
|
#endif
|
||||||
|
{false, _, "0x000000000000000000000000000000000000000"}, // too long
|
||||||
|
{false, _, "x"}, // leading x
|
||||||
|
{false, _, "0x"}, // empty number
|
||||||
|
{false, _, "0xx"} // extra x
|
||||||
|
};
|
||||||
|
for (const auto& td : test_data) {
|
||||||
|
Json::Value root;
|
||||||
|
Json::String errs;
|
||||||
|
const char* c0 = td.in.c_str();
|
||||||
|
const char* c1 = c0 + td.in.size();
|
||||||
|
bool ok = reader->parse(c0, c1, &root, &errs);
|
||||||
|
JSONTEST_ASSERT(td.ok == ok) << "in: " << td.in;
|
||||||
|
if (td.ok)
|
||||||
|
{
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT(root.isUInt64());
|
||||||
|
if (root.isUInt64())
|
||||||
|
JSONTEST_ASSERT_EQUAL(td.out, root.asLargestUInt());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSONTEST_ASSERT(errs.size() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct CharReaderAllowSpecialFloatsTest : JsonTest::TestCase {};
|
struct CharReaderAllowSpecialFloatsTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, specialFloat) {
|
JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, specialFloat) {
|
||||||
|
Loading…
Reference in New Issue
Block a user