JSON parser wasn't handling ulong values correctly.

It passed all scalar ints thru a int64_t, which would truncate
uint64_t values with the upper bit set.

Change-Id: I38fb8c68c911ae44d9863f8e35c2429ca0ab51e5
Tested: on Linux.
parent 2dd6ba57
...@@ -520,7 +520,7 @@ class Parser : public ParserState { ...@@ -520,7 +520,7 @@ class Parser : public ParserState {
private: private:
FLATBUFFERS_CHECKED_ERROR Error(const std::string &msg); FLATBUFFERS_CHECKED_ERROR Error(const std::string &msg);
FLATBUFFERS_CHECKED_ERROR ParseHexNum(int nibbles, int64_t *val); FLATBUFFERS_CHECKED_ERROR ParseHexNum(int nibbles, uint64_t *val);
FLATBUFFERS_CHECKED_ERROR Next(); FLATBUFFERS_CHECKED_ERROR Next();
FLATBUFFERS_CHECKED_ERROR SkipByteOrderMark(); FLATBUFFERS_CHECKED_ERROR SkipByteOrderMark();
bool Is(int t); bool Is(int t);
......
...@@ -95,7 +95,8 @@ inline std::string IntToStringHex(int i, int xdigits) { ...@@ -95,7 +95,8 @@ inline std::string IntToStringHex(int i, int xdigits) {
} }
// Portable implementation of strtoll(). // Portable implementation of strtoll().
inline int64_t StringToInt(const char *str, char **endptr = nullptr, int base = 10) { inline int64_t StringToInt(const char *str, char **endptr = nullptr,
int base = 10) {
#ifdef _MSC_VER #ifdef _MSC_VER
return _strtoi64(str, endptr, base); return _strtoi64(str, endptr, base);
#else #else
...@@ -104,7 +105,8 @@ inline int64_t StringToInt(const char *str, char **endptr = nullptr, int base = ...@@ -104,7 +105,8 @@ inline int64_t StringToInt(const char *str, char **endptr = nullptr, int base =
} }
// Portable implementation of strtoull(). // Portable implementation of strtoull().
inline int64_t StringToUInt(const char *str, char **endptr = nullptr, int base = 10) { inline uint64_t StringToUInt(const char *str, char **endptr = nullptr,
int base = 10) {
#ifdef _MSC_VER #ifdef _MSC_VER
return _strtoui64(str, endptr, base); return _strtoui64(str, endptr, base);
#else #else
......
...@@ -109,6 +109,12 @@ template<typename T> inline CheckedError atot(const char *s, Parser &parser, ...@@ -109,6 +109,12 @@ template<typename T> inline CheckedError atot(const char *s, Parser &parser,
*val = (T)i; *val = (T)i;
return NoError(); return NoError();
} }
template<> inline CheckedError atot<uint64_t>(const char *s, Parser &parser,
uint64_t *val) {
(void)parser;
*val = StringToUInt(s);
return NoError();
}
template<> inline CheckedError atot<bool>(const char *s, Parser &parser, template<> inline CheckedError atot<bool>(const char *s, Parser &parser,
bool *val) { bool *val) {
(void)parser; (void)parser;
...@@ -213,7 +219,7 @@ std::string Parser::TokenToStringId(int t) { ...@@ -213,7 +219,7 @@ std::string Parser::TokenToStringId(int t) {
} }
// Parses exactly nibbles worth of hex digits into a number, or error. // Parses exactly nibbles worth of hex digits into a number, or error.
CheckedError Parser::ParseHexNum(int nibbles, int64_t *val) { CheckedError Parser::ParseHexNum(int nibbles, uint64_t *val) {
for (int i = 0; i < nibbles; i++) for (int i = 0; i < nibbles; i++)
if (!isxdigit(static_cast<const unsigned char>(cursor_[i]))) if (!isxdigit(static_cast<const unsigned char>(cursor_[i])))
return Error("escape code must be followed by " + NumToString(nibbles) + return Error("escape code must be followed by " + NumToString(nibbles) +
...@@ -280,14 +286,14 @@ CheckedError Parser::Next() { ...@@ -280,14 +286,14 @@ CheckedError Parser::Next() {
case '/': attribute_ += '/'; cursor_++; break; case '/': attribute_ += '/'; cursor_++; break;
case 'x': { // Not in the JSON standard case 'x': { // Not in the JSON standard
cursor_++; cursor_++;
int64_t val; uint64_t val;
ECHECK(ParseHexNum(2, &val)); ECHECK(ParseHexNum(2, &val));
attribute_ += static_cast<char>(val); attribute_ += static_cast<char>(val);
break; break;
} }
case 'u': { case 'u': {
cursor_++; cursor_++;
int64_t val; uint64_t val;
ECHECK(ParseHexNum(4, &val)); ECHECK(ParseHexNum(4, &val));
if (val >= 0xD800 && val <= 0xDBFF) { if (val >= 0xD800 && val <= 0xDBFF) {
if (unicode_high_surrogate != -1) { if (unicode_high_surrogate != -1) {
...@@ -442,7 +448,8 @@ CheckedError Parser::Next() { ...@@ -442,7 +448,8 @@ CheckedError Parser::Next() {
return NoError(); return NoError();
} else if (isdigit(static_cast<unsigned char>(c)) || c == '-') { } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
const char *start = cursor_ - 1; const char *start = cursor_ - 1;
if (c == '-' && *cursor_ == '0' && (cursor_[1] == 'x' || cursor_[1] == 'X')) { if (c == '-' && *cursor_ == '0' &&
(cursor_[1] == 'x' || cursor_[1] == 'X')) {
++start; ++start;
++cursor_; ++cursor_;
attribute_.append(&c, &c + 1); attribute_.append(&c, &c + 1);
...@@ -452,7 +459,8 @@ CheckedError Parser::Next() { ...@@ -452,7 +459,8 @@ CheckedError Parser::Next() {
cursor_++; cursor_++;
while (isxdigit(static_cast<unsigned char>(*cursor_))) cursor_++; while (isxdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
attribute_.append(start + 2, cursor_); attribute_.append(start + 2, cursor_);
attribute_ = NumToString(StringToUInt(attribute_.c_str(), nullptr, 16)); attribute_ = NumToString(static_cast<int64_t>(
StringToUInt(attribute_.c_str(), nullptr, 16)));
token_ = kTokenIntegerConstant; token_ = kTokenIntegerConstant;
return NoError(); return NoError();
} }
......
...@@ -994,14 +994,13 @@ template<typename T> T TestValue(const char *json, const char *type_name) { ...@@ -994,14 +994,13 @@ template<typename T> T TestValue(const char *json, const char *type_name) {
flatbuffers::Parser parser; flatbuffers::Parser parser;
// Simple schema. // Simple schema.
TEST_EQ(parser.Parse(std::string("table X { Y:" + std::string(type_name) + "; } root_type X;").c_str()), true); TEST_EQ(parser.Parse(std::string("table X { Y:" + std::string(type_name) +
"; } root_type X;").c_str()), true);
TEST_EQ(parser.Parse(json), true); TEST_EQ(parser.Parse(json), true);
auto root = flatbuffers::GetRoot<T>(parser.builder_.GetBufferPointer()); auto root = flatbuffers::GetRoot<flatbuffers::Table>(
// root will point to the table, which is a 32bit vtable offset followed parser.builder_.GetBufferPointer());
// by a float: return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
TEST_EQ(sizeof(flatbuffers::soffset_t), 4); // Test assumes 32bit offsets
return root[1];
} }
bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
...@@ -1009,13 +1008,19 @@ bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } ...@@ -1009,13 +1008,19 @@ bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; }
// Additional parser testing not covered elsewhere. // Additional parser testing not covered elsewhere.
void ValueTest() { void ValueTest() {
// Test scientific notation numbers. // Test scientific notation numbers.
TEST_EQ(FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }","float"), (float)3.14159), true); TEST_EQ(FloatCompare(TestValue<float>("{ Y:0.0314159e+2 }","float"),
(float)3.14159), true);
// Test conversion functions. // Test conversion functions.
TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }","float"), -1), true); TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }","float"), -1),
true);
// Test negative hex constant. // Test negative hex constant.
TEST_EQ(TestValue<int>("{ Y:-0x80 }","int") == -128, true); TEST_EQ(TestValue<int>("{ Y:-0x80 }","int"), -128);
// Make sure we do unsigned 64bit correctly.
TEST_EQ(TestValue<uint64_t>("{ Y:12335089644688340133 }","ulong"),
12335089644688340133ULL);
} }
void EnumStringsTest() { void EnumStringsTest() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment