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
|
||||
/dist/
|
||||
/.cache/
|
||||
/.vscode/
|
||||
|
||||
# MSVC project files:
|
||||
*.sln
|
||||
|
@ -72,7 +72,7 @@ project(jsoncpp
|
||||
# 2. ./include/json/version.h
|
||||
# 3. ./CMakeLists.txt
|
||||
# IMPORTANT: also update the PROJECT_SOVERSION!!
|
||||
VERSION 1.9.5 # <major>[.<minor>[.<patch>[.<tweak>]]]
|
||||
VERSION 1.10.0 # <major>[.<minor>[.<patch>[.<tweak>]]]
|
||||
LANGUAGES CXX)
|
||||
|
||||
message(STATUS "JsonCpp Version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
@ -324,6 +324,8 @@ public:
|
||||
* - `"allowSpecialFloats": false or true`
|
||||
* - If true, special float values (NaNs and infinities) are allowed and
|
||||
* 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`
|
||||
* - If true, if the input starts with the Unicode byte order mark (BOM),
|
||||
* it is skipped.
|
||||
|
@ -875,6 +875,7 @@ public:
|
||||
bool failIfExtra_;
|
||||
bool rejectDupKeys_;
|
||||
bool allowSpecialFloats_;
|
||||
bool allowHexadecimal_;
|
||||
bool skipBom_;
|
||||
size_t stackLimit_;
|
||||
}; // OurFeatures
|
||||
@ -914,6 +915,7 @@ private:
|
||||
tokenArrayEnd,
|
||||
tokenString,
|
||||
tokenNumber,
|
||||
tokenHexadecimal,
|
||||
tokenTrue,
|
||||
tokenFalse,
|
||||
tokenNull,
|
||||
@ -952,11 +954,14 @@ private:
|
||||
bool readString();
|
||||
bool readStringSingleQuote();
|
||||
bool readNumber(bool checkInf);
|
||||
bool readHexadecimal();
|
||||
bool readValue();
|
||||
bool readObject(Token& token);
|
||||
bool readArray(Token& token);
|
||||
bool decodeNumber(Token& token);
|
||||
bool decodeNumber(Token& token, Value& decoded);
|
||||
bool decodeHexadecimal(Token& token);
|
||||
bool decodeHexadecimal(Token& token, Value& decoded);
|
||||
bool decodeString(Token& token);
|
||||
bool decodeString(Token& token, String& decoded);
|
||||
bool decodeDouble(Token& token);
|
||||
@ -1078,6 +1083,9 @@ bool OurReader::readValue() {
|
||||
case tokenNumber:
|
||||
successful = decodeNumber(token);
|
||||
break;
|
||||
case tokenHexadecimal:
|
||||
successful = decodeHexadecimal(token);
|
||||
break;
|
||||
case tokenString:
|
||||
successful = decodeString(token);
|
||||
break;
|
||||
@ -1191,6 +1199,16 @@ bool OurReader::readToken(Token& token) {
|
||||
ok = readComment();
|
||||
break;
|
||||
case '0':
|
||||
if(match("x", 1)) {
|
||||
token.type_ = tokenHexadecimal;
|
||||
ok = features_.allowHexadecimal_;
|
||||
readHexadecimal();
|
||||
}
|
||||
else {
|
||||
token.type_ = tokenNumber;
|
||||
readNumber(false);
|
||||
}
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
@ -1419,6 +1437,20 @@ bool OurReader::readNumber(bool checkInf) {
|
||||
}
|
||||
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() {
|
||||
Char c = 0;
|
||||
while (current_ != end_) {
|
||||
@ -1639,6 +1671,41 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) {
|
||||
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) {
|
||||
Value decoded;
|
||||
if (!decodeDouble(token, decoded))
|
||||
@ -1908,6 +1975,7 @@ CharReader* CharReaderBuilder::newCharReader() const {
|
||||
features.failIfExtra_ = settings_["failIfExtra"].asBool();
|
||||
features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
|
||||
features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
|
||||
features.allowHexadecimal_ = settings_["allowHexadecimal"].asBool();
|
||||
features.skipBom_ = settings_["skipBom"].asBool();
|
||||
return new OurCharReader(collectComments, features);
|
||||
}
|
||||
@ -1925,6 +1993,7 @@ bool CharReaderBuilder::validate(Json::Value* invalid) const {
|
||||
"failIfExtra",
|
||||
"rejectDupKeys",
|
||||
"allowSpecialFloats",
|
||||
"allowHexadecimal",
|
||||
"skipBom",
|
||||
};
|
||||
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 {};
|
||||
|
||||
JSONTEST_FIXTURE_LOCAL(CharReaderAllowSpecialFloatsTest, specialFloat) {
|
||||
|
Loading…
Reference in New Issue
Block a user