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

This commit is contained in:
Benoit Blanchon
2016-04-28 08:42:59 +02:00
parent f192d5c12e
commit f9f002c8f7
11 changed files with 125 additions and 78 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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