JSON parser wasn't handling ulong values correctly.
authorWouter van Oortmerssen <aardappel@gmail.com>
Tue, 31 Jan 2017 01:07:55 +0000 (17:07 -0800)
committerWouter van Oortmerssen <aardappel@gmail.com>
Wed, 1 Feb 2017 22:18:28 +0000 (14:18 -0800)
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.

include/flatbuffers/idl.h
include/flatbuffers/util.h
src/idl_parser.cpp
tests/test.cpp

index 898b410..0956d18 100644 (file)
@@ -520,7 +520,7 @@ class Parser : public ParserState {
 
 private:
   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 SkipByteOrderMark();
   bool Is(int t);
index c6755ab..2bd0ae0 100644 (file)
@@ -95,7 +95,8 @@ inline std::string IntToStringHex(int i, int xdigits) {
 }
 
 // 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
     return _strtoi64(str, endptr, base);
   #else
@@ -104,7 +105,8 @@ inline int64_t StringToInt(const char *str, char **endptr = nullptr, int base =
 }
 
 // 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
     return _strtoui64(str, endptr, base);
   #else
index 5974d04..454abd3 100644 (file)
@@ -109,6 +109,12 @@ template<typename T> inline CheckedError atot(const char *s, Parser &parser,
   *val = (T)i;
   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,
                                           bool *val) {
   (void)parser;
@@ -213,7 +219,7 @@ std::string Parser::TokenToStringId(int t) {
 }
 
 // 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++)
     if (!isxdigit(static_cast<const unsigned char>(cursor_[i])))
       return Error("escape code must be followed by " + NumToString(nibbles) +
@@ -280,14 +286,14 @@ CheckedError Parser::Next() {
               case '/':  attribute_ += '/';  cursor_++; break;
               case 'x': {  // Not in the JSON standard
                 cursor_++;
-                int64_t val;
+                uint64_t val;
                 ECHECK(ParseHexNum(2, &val));
                 attribute_ += static_cast<char>(val);
                 break;
               }
               case 'u': {
                 cursor_++;
-                int64_t val;
+                uint64_t val;
                 ECHECK(ParseHexNum(4, &val));
                 if (val >= 0xD800 && val <= 0xDBFF) {
                   if (unicode_high_surrogate != -1) {
@@ -442,7 +448,8 @@ CheckedError Parser::Next() {
           return NoError();
         } else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
           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;
             ++cursor_;
             attribute_.append(&c, &c + 1);
@@ -452,7 +459,8 @@ CheckedError Parser::Next() {
               cursor_++;
               while (isxdigit(static_cast<unsigned char>(*cursor_))) 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;
               return NoError();
           }
index dc85072..3484104 100644 (file)
@@ -491,7 +491,7 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) {
   TEST_NOTNULL(pos_table_ptr);
   TEST_EQ_STR(pos_table_ptr->name()->c_str(), "MyGame.Example.Vec3");
 
-  
+
 
   // Now use it to dynamically access a buffer.
   auto &root = *flatbuffers::GetAnyRoot(flatbuf);
@@ -607,11 +607,11 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) {
         reinterpret_cast<const uint8_t *>(resizingbuf.data()),
         resizingbuf.size());
   TEST_EQ(VerifyMonsterBuffer(resize_verifier), true);
-    
+
   // Test buffer is valid using reflection as well
   TEST_EQ(flatbuffers::Verify(schema, *schema.root_table(), resizingbuf.data(),
                               resizingbuf.size()), true);
-  
+
   // As an additional test, also set it on the name field.
   // Note: unlike the name change above, this just overwrites the offset,
   // rather than changing the string in-place.
@@ -994,14 +994,13 @@ template<typename T> T TestValue(const char *json, const char *type_name) {
   flatbuffers::Parser parser;
 
   // 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);
-  auto root = flatbuffers::GetRoot<T>(parser.builder_.GetBufferPointer());
-  // root will point to the table, which is a 32bit vtable offset followed
-  // by a float:
-  TEST_EQ(sizeof(flatbuffers::soffset_t), 4);  // Test assumes 32bit offsets
-  return root[1];
+  auto root = flatbuffers::GetRoot<flatbuffers::Table>(
+                parser.builder_.GetBufferPointer());
+  return root->GetField<T>(flatbuffers::FieldIndexToOffset(0), 0);
 }
 
 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.
 void ValueTest() {
   // 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_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_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() {