From f9f002c8f7cd23a2bb686fcea54f4675eaa061cd Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 28 Apr 2016 08:42:59 +0200 Subject: [PATCH] Fix `unsigned long` printed as `signed long` (issue #170) --- CHANGELOG.md | 5 ++ include/ArduinoJson/Arduino/Print.hpp | 12 +--- include/ArduinoJson/Internals/JsonInteger.hpp | 3 + .../Internals/JsonVariantContent.hpp | 10 +-- .../ArduinoJson/Internals/JsonVariantType.hpp | 16 +++-- include/ArduinoJson/Internals/JsonWriter.hpp | 34 +++++----- include/ArduinoJson/JsonVariant.hpp | 9 ++- include/ArduinoJson/JsonVariant.ipp | 28 ++++---- src/JsonVariant.cpp | 68 ++++++++++++------- test/JsonParser_Array_Tests.cpp | 8 +++ test/JsonVariant_PrintTo_Tests.cpp | 10 +++ 11 files changed, 125 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf3495d..f71d1b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Fix `unsigned long` printed as `signed long` (issue #170) + v5.2.0 ------ diff --git a/include/ArduinoJson/Arduino/Print.hpp b/include/ArduinoJson/Arduino/Print.hpp index 3ec2e90a..77592355 100644 --- a/include/ArduinoJson/Arduino/Print.hpp +++ b/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(value % 10 + '0'); value /= 10; - buffer[i++] = static_cast(digit >= 0 ? '0' + digit : '0' - digit); } while (value); + size_t n = 0; while (i > 0) { n += write(buffer[--i]); } diff --git a/include/ArduinoJson/Internals/JsonInteger.hpp b/include/ArduinoJson/Internals/JsonInteger.hpp index af2879f0..f273213c 100644 --- a/include/ArduinoJson/Internals/JsonInteger.hpp +++ b/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 } } diff --git a/include/ArduinoJson/Internals/JsonVariantContent.hpp b/include/ArduinoJson/Internals/JsonVariantContent.hpp index 23291c08..77ae503c 100644 --- a/include/ArduinoJson/Internals/JsonVariantContent.hpp +++ b/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 }; } } diff --git a/include/ArduinoJson/Internals/JsonVariantType.hpp b/include/ArduinoJson/Internals/JsonVariantType.hpp index b09ca1bf..7af4c86b 100644 --- a/include/ArduinoJson/Internals/JsonVariantType.hpp +++ b/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 diff --git a/include/ArduinoJson/Internals/JsonWriter.hpp b/include/ArduinoJson/Internals/JsonWriter.hpp index f201ca48..53bd105c 100644 --- a/include/ArduinoJson/Internals/JsonWriter.hpp +++ b/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; diff --git a/include/ArduinoJson/JsonVariant.hpp b/include/ArduinoJson/JsonVariant.hpp index 512c6108..d1d384f4 100644 --- a/include/ArduinoJson/JsonVariant.hpp +++ b/include/ArduinoJson/JsonVariant.hpp @@ -72,8 +72,13 @@ class JsonVariant : public JsonVariantBase { typename TypeTraits::EnableIf::value>::type * = 0) { using namespace Internals; - _type = JSON_INTEGER; - _content.asInteger = static_cast(value); + if (value >= 0) { + _type = JSON_POSITIVE_INTEGER; + _content.asInteger = static_cast(value); + } else { + _type = JSON_NEGATIVE_INTEGER; + _content.asInteger = static_cast(-value); + } } // Create a JsonVariant containing a string. diff --git a/include/ArduinoJson/JsonVariant.ipp b/include/ArduinoJson/JsonVariant.ipp index 32f9b7b9..46697ad3 100644 --- a/include/ArduinoJson/JsonVariant.ipp +++ b/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(_content.asFloat); - - if ((_type == Internals::JSON_STRING || _type == Internals::JSON_UNPARSED) && - _content.asString) { - if (!strcmp("true", _content.asString)) return 1; - return Internals::parse(_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(_content.asString); + default: + return static_cast(_content.asFloat); } - - return 0L; } #if ARDUINOJSON_ENABLE_STD_STREAM diff --git a/src/JsonVariant.cpp b/src/JsonVariant.cpp index 949a0b08..2f9e5f71 100644 --- a/src/JsonVariant.cpp +++ b/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(_content.asInteger); - - if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) - return parse(_content.asString); - - return 0.0; + switch (_type) { + case JSON_UNDEFINED: + return 0; + case JSON_POSITIVE_INTEGER: + case JSON_BOOLEAN: + return static_cast(_content.asInteger); + case JSON_NEGATIVE_INTEGER: + return -static_cast(_content.asInteger); + case JSON_STRING: + case JSON_UNPARSED: + return _content.asString ? parse(_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); + switch (_type) { + case JSON_UNDEFINED: + return; - else if (_type == JSON_OBJECT) - _content.asObject->writeTo(writer); + case JSON_ARRAY: + _content.asArray->writeTo(writer); + return; - else if (_type == JSON_STRING) - writer.writeString(_content.asString); + case JSON_OBJECT: + _content.asObject->writeTo(writer); + return; - else if (_type == JSON_UNPARSED) - writer.writeRaw(_content.asString); + case JSON_STRING: + writer.writeString(_content.asString); + return; - else if (_type == JSON_INTEGER) - writer.writeInteger(_content.asInteger); + case JSON_UNPARSED: + writer.writeRaw(_content.asString); + return; - else if (_type == JSON_BOOLEAN) - writer.writeBoolean(_content.asInteger != 0); + case JSON_NEGATIVE_INTEGER: + writer.writeRaw('-'); + case JSON_POSITIVE_INTEGER: + writer.writeInteger(_content.asInteger); + return; - else if (_type >= JSON_FLOAT_0_DECIMALS) { - uint8_t decimals = static_cast(_type - JSON_FLOAT_0_DECIMALS); - writer.writeFloat(_content.asFloat, decimals); + case JSON_BOOLEAN: + writer.writeBoolean(_content.asInteger != 0); + return; + + default: + uint8_t decimals = static_cast(_type - JSON_FLOAT_0_DECIMALS); + writer.writeFloat(_content.asFloat, decimals); } } } diff --git a/test/JsonParser_Array_Tests.cpp b/test/JsonParser_Array_Tests.cpp index 5d4eeeca..4b69e1a5 100644 --- a/test/JsonParser_Array_Tests.cpp +++ b/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]"); diff --git a/test/JsonVariant_PrintTo_Tests.cpp b/test/JsonVariant_PrintTo_Tests.cpp index ed031159..aca8c666 100644 --- a/test/JsonVariant_PrintTo_Tests.cpp +++ b/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