Просмотр исходного кода

Fix `unsigned long` printed as `signed long` (issue #170)

Benoit Blanchon 9 лет назад
Родитель
Сommit
f9f002c8f7

+ 5 - 0
CHANGELOG.md

@@ -1,6 +1,11 @@
 ArduinoJson: change log
 =======================
 
+HEAD
+----
+
+* Fix `unsigned long` printed as `signed long` (issue #170)
+
 v5.2.0
 ------
 

+ 3 - 9
include/ArduinoJson/Arduino/Print.hpp

@@ -56,22 +56,16 @@ class Print {
     return print(tmp);
   }
 
-  size_t print(ArduinoJson::Internals::JsonInteger value) {
-    // see http://clc-wiki.net/wiki/K%26R2_solutions:Chapter_3:Exercise_4
+  size_t print(ArduinoJson::Internals::JsonUInt value) {
     char buffer[22];
 
-    size_t n = 0;
-    if (value < 0) {
-      value = -value;
-      n += write('-');
-    }
     uint8_t i = 0;
     do {
-      ArduinoJson::Internals::JsonInteger digit = value % 10;
+      buffer[i++] = static_cast<char>(value % 10 + '0');
       value /= 10;
-      buffer[i++] = static_cast<char>(digit >= 0 ? '0' + digit : '0' - digit);
     } while (value);
 
+    size_t n = 0;
     while (i > 0) {
       n += write(buffer[--i]);
     }

+ 3 - 0
include/ArduinoJson/Internals/JsonInteger.hpp

@@ -14,10 +14,13 @@ namespace Internals {
 
 #if ARDUINOJSON_USE_LONG_LONG
 typedef long long JsonInteger;
+typedef unsigned long long JsonUInt;
 #elif ARDUINOJSON_USE_INT64
 typedef __int64 JsonInteger;
+typedef unsigned _int64 JsonUInt;
 #else
 typedef long JsonInteger;
+typedef unsigned long JsonUInt;
 #endif
 }
 }

+ 5 - 5
include/ArduinoJson/Internals/JsonVariantContent.hpp

@@ -20,11 +20,11 @@ namespace Internals {
 // A union that defines the actual content of a JsonVariant.
 // The enum JsonVariantType determines which member is in use.
 union JsonVariantContent {
-  JsonFloat asFloat;      // used for double and float
-  JsonInteger asInteger;  // used for bool, char, short, int and longs
-  const char* asString;   // asString can be null
-  JsonArray* asArray;     // asArray cannot be null
-  JsonObject* asObject;   // asObject cannot be null
+  JsonFloat asFloat;     // used for double and float
+  JsonUInt asInteger;    // used for bool, char, short, int and longs
+  const char* asString;  // asString can be null
+  JsonArray* asArray;    // asArray cannot be null
+  JsonObject* asObject;  // asObject cannot be null
 };
 }
 }

+ 9 - 7
include/ArduinoJson/Internals/JsonVariantType.hpp

@@ -16,13 +16,15 @@ namespace Internals {
 // Enumerated type to know the current type of a JsonVariant.
 // The value determines which member of JsonVariantContent is used.
 enum JsonVariantType {
-  JSON_UNDEFINED,  // the JsonVariant has not been initialized
-  JSON_UNPARSED,   // the JsonVariant contains an unparsed string
-  JSON_STRING,     // the JsonVariant stores a const char*
-  JSON_BOOLEAN,    // the JsonVariant stores a bool
-  JSON_INTEGER,    // the JsonVariant stores an integer
-  JSON_ARRAY,      // the JsonVariant stores a pointer to a JsonArray
-  JSON_OBJECT,     // the JsonVariant stores a pointer to a JsonObject
+  JSON_UNDEFINED,         // JsonVariant has not been initialized
+  JSON_UNPARSED,          // JsonVariant contains an unparsed string
+  JSON_STRING,            // JsonVariant stores a const char*
+  JSON_BOOLEAN,           // JsonVariant stores a bool
+  JSON_POSITIVE_INTEGER,  // JsonVariant stores an unsigned long
+  JSON_NEGATIVE_INTEGER,  // JsonVariant stores an unsigned long that must be
+                          // negated
+  JSON_ARRAY,             // JsonVariant stores a pointer to a JsonArray
+  JSON_OBJECT,            // JsonVariant stores a pointer to a JsonObject
 
   // The following values are reserved for float values
   // Multiple values are used for double, depending on the number of decimal

+ 16 - 18
include/ArduinoJson/Internals/JsonWriter.hpp

@@ -32,49 +32,47 @@ class JsonWriter {
   // number of bytes written.
   size_t bytesWritten() const { return _length; }
 
-  void beginArray() { write('['); }
-  void endArray() { write(']'); }
+  void beginArray() { writeRaw('['); }
+  void endArray() { writeRaw(']'); }
 
-  void beginObject() { write('{'); }
-  void endObject() { write('}'); }
+  void beginObject() { writeRaw('{'); }
+  void endObject() { writeRaw('}'); }
 
-  void writeColon() { write(':'); }
-  void writeComma() { write(','); }
+  void writeColon() { writeRaw(':'); }
+  void writeComma() { writeRaw(','); }
 
-  void writeBoolean(bool value) { write(value ? "true" : "false"); }
+  void writeBoolean(bool value) { writeRaw(value ? "true" : "false"); }
 
   void writeString(const char *value) {
     if (!value) {
-      write("null");
+      writeRaw("null");
     } else {
-      write('\"');
+      writeRaw('\"');
       while (*value) writeChar(*value++);
-      write('\"');
+      writeRaw('\"');
     }
   }
 
   void writeChar(char c) {
     char specialChar = Encoding::escapeChar(c);
     if (specialChar) {
-      write('\\');
-      write(specialChar);
+      writeRaw('\\');
+      writeRaw(specialChar);
     } else {
-      write(c);
+      writeRaw(c);
     }
   }
 
-  void writeInteger(JsonInteger value) { _length += _sink.print(value); }
+  void writeInteger(JsonUInt value) { _length += _sink.print(value); }
 
   void writeFloat(JsonFloat value, uint8_t decimals) {
     _length += _sink.print(value, decimals);
   }
 
-  void writeRaw(const char *s) { return write(s); }
+  void writeRaw(const char *s) { _length += _sink.print(s); }
+  void writeRaw(char c) { _length += _sink.write(c); }
 
  protected:
-  void write(char c) { _length += _sink.write(c); }
-  FORCE_INLINE void write(const char *s) { _length += _sink.print(s); }
-
   Print &_sink;
   size_t _length;
 

+ 7 - 2
include/ArduinoJson/JsonVariant.hpp

@@ -72,8 +72,13 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
       typename TypeTraits::EnableIf<TypeTraits::IsIntegral<T>::value>::type * =
           0) {
     using namespace Internals;
-    _type = JSON_INTEGER;
-    _content.asInteger = static_cast<JsonInteger>(value);
+    if (value >= 0) {
+      _type = JSON_POSITIVE_INTEGER;
+      _content.asInteger = static_cast<JsonInteger>(value);
+    } else {
+      _type = JSON_NEGATIVE_INTEGER;
+      _content.asInteger = static_cast<JsonInteger>(-value);
+    }
   }
 
   // Create a JsonVariant containing a string.

+ 16 - 12
include/ArduinoJson/JsonVariant.ipp

@@ -47,19 +47,23 @@ inline T JsonVariant::invalid() {
 }
 
 inline Internals::JsonInteger JsonVariant::asInteger() const {
-  if (_type == Internals::JSON_INTEGER || _type == Internals::JSON_BOOLEAN)
-    return _content.asInteger;
-
-  if (_type >= Internals::JSON_FLOAT_0_DECIMALS)
-    return static_cast<Internals::JsonInteger>(_content.asFloat);
-
-  if ((_type == Internals::JSON_STRING || _type == Internals::JSON_UNPARSED) &&
-      _content.asString) {
-    if (!strcmp("true", _content.asString)) return 1;
-    return Internals::parse<Internals::JsonInteger>(_content.asString);
+  using namespace Internals;
+  switch (_type) {
+    case JSON_UNDEFINED:
+      return 0;
+    case JSON_POSITIVE_INTEGER:
+    case JSON_BOOLEAN:
+      return _content.asInteger;
+    case JSON_NEGATIVE_INTEGER:
+      return -_content.asInteger;
+    case JSON_STRING:
+    case JSON_UNPARSED:
+      if (!_content.asString) return 0;
+      if (!strcmp("true", _content.asString)) return 1;
+      return parse<Internals::JsonInteger>(_content.asString);
+    default:
+      return static_cast<Internals::JsonInteger>(_content.asFloat);
   }
-
-  return 0L;
 }
 
 #if ARDUINOJSON_ENABLE_STD_STREAM

+ 49 - 31
src/JsonVariant.cpp

@@ -26,15 +26,20 @@ const char *JsonVariant::asString() const {
 }
 
 JsonFloat JsonVariant::asFloat() const {
-  if (_type >= JSON_FLOAT_0_DECIMALS) return _content.asFloat;
-
-  if (_type == JSON_INTEGER || _type == JSON_BOOLEAN)
-    return static_cast<JsonFloat>(_content.asInteger);
-
-  if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString)
-    return parse<JsonFloat>(_content.asString);
-
-  return 0.0;
+  switch (_type) {
+    case JSON_UNDEFINED:
+      return 0;
+    case JSON_POSITIVE_INTEGER:
+    case JSON_BOOLEAN:
+      return static_cast<JsonFloat>(_content.asInteger);
+    case JSON_NEGATIVE_INTEGER:
+      return -static_cast<JsonFloat>(_content.asInteger);
+    case JSON_STRING:
+    case JSON_UNPARSED:
+      return _content.asString ? parse<JsonFloat>(_content.asString) : 0;
+    default:
+      return _content.asFloat;
+  }
 }
 
 String JsonVariant::toString() const {
@@ -57,7 +62,8 @@ bool JsonVariant::isBoolean() const {
 }
 
 bool JsonVariant::isInteger() const {
-  if (_type == JSON_INTEGER) return true;
+  if (_type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER)
+    return true;
 
   if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
 
@@ -81,27 +87,39 @@ bool JsonVariant::isFloat() const {
 }
 
 void JsonVariant::writeTo(JsonWriter &writer) const {
-  if (_type == JSON_ARRAY)
-    _content.asArray->writeTo(writer);
-
-  else if (_type == JSON_OBJECT)
-    _content.asObject->writeTo(writer);
-
-  else if (_type == JSON_STRING)
-    writer.writeString(_content.asString);
-
-  else if (_type == JSON_UNPARSED)
-    writer.writeRaw(_content.asString);
-
-  else if (_type == JSON_INTEGER)
-    writer.writeInteger(_content.asInteger);
-
-  else if (_type == JSON_BOOLEAN)
-    writer.writeBoolean(_content.asInteger != 0);
-
-  else if (_type >= JSON_FLOAT_0_DECIMALS) {
-    uint8_t decimals = static_cast<uint8_t>(_type - JSON_FLOAT_0_DECIMALS);
-    writer.writeFloat(_content.asFloat, decimals);
+  switch (_type) {
+    case JSON_UNDEFINED:
+      return;
+
+    case JSON_ARRAY:
+      _content.asArray->writeTo(writer);
+      return;
+
+    case JSON_OBJECT:
+      _content.asObject->writeTo(writer);
+      return;
+
+    case JSON_STRING:
+      writer.writeString(_content.asString);
+      return;
+
+    case JSON_UNPARSED:
+      writer.writeRaw(_content.asString);
+      return;
+
+    case JSON_NEGATIVE_INTEGER:
+      writer.writeRaw('-');
+    case JSON_POSITIVE_INTEGER:
+      writer.writeInteger(_content.asInteger);
+      return;
+
+    case JSON_BOOLEAN:
+      writer.writeBoolean(_content.asInteger != 0);
+      return;
+
+    default:
+      uint8_t decimals = static_cast<uint8_t>(_type - JSON_FLOAT_0_DECIMALS);
+      writer.writeFloat(_content.asFloat, decimals);
   }
 }
 }

+ 8 - 0
test/JsonParser_Array_Tests.cpp

@@ -125,6 +125,14 @@ TEST_F(JsonParser_Array_Tests, TwoDoubles) {
   secondElementMustBe(1e2);
 }
 
+TEST_F(JsonParser_Array_Tests, UnsignedLong) {
+  whenInputIs("[4294967295]");
+
+  parseMustSucceed();
+  sizeMustBe(1);
+  firstElementMustBe(4294967295UL);
+}
+
 TEST_F(JsonParser_Array_Tests, TwoBooleans) {
   whenInputIs("[true,false]");
 

+ 10 - 0
test/JsonVariant_PrintTo_Tests.cpp

@@ -57,6 +57,11 @@ TEST_F(JsonVariant_PrintTo_Tests, Long) {
   outputMustBe("42");
 }
 
+TEST_F(JsonVariant_PrintTo_Tests, UnsignedLong) {
+  variant = 4294967295UL;
+  outputMustBe("4294967295");
+}
+
 TEST_F(JsonVariant_PrintTo_Tests, Char) {
   variant = '*';
   outputMustBe("42");
@@ -82,4 +87,9 @@ TEST_F(JsonVariant_PrintTo_Tests, PositiveInt64) {
   variant = 9223372036854775807;
   outputMustBe("9223372036854775807");
 }
+
+TEST_F(JsonVariant_PrintTo_Tests, UInt64) {
+  variant = 18446744073709551615;
+  outputMustBe("18446744073709551615");
+}
 #endif