diff --git a/CHANGELOG.md b/CHANGELOG.md index 713ff4b4..6dfbd6bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ v5.0.5 ------ * Add overload `JsonObjectSuscript::set(value, decimals)` (issue #143) +* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134) v5.0.4 ------ diff --git a/README.md b/README.md index ea42e8a9..c54ec0e7 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ data.add(2.302038, 6); // if not specified, 2 digits are printed root.printTo(Serial); // This prints: // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -`` +``` Documentation diff --git a/include/ArduinoJson/Arduino/Print.hpp b/include/ArduinoJson/Arduino/Print.hpp index acdc1a19..93f94539 100644 --- a/include/ArduinoJson/Arduino/Print.hpp +++ b/include/ArduinoJson/Arduino/Print.hpp @@ -20,6 +20,7 @@ class Print { size_t print(const char[]); size_t print(double, int = 2); + size_t print(int); size_t print(long); size_t println(); }; diff --git a/include/ArduinoJson/Arduino/String.hpp b/include/ArduinoJson/Arduino/String.hpp index d4384f4d..45904deb 100644 --- a/include/ArduinoJson/Arduino/String.hpp +++ b/include/ArduinoJson/Arduino/String.hpp @@ -16,6 +16,7 @@ class String : public std::string { String(const char *cstr = "") : std::string(cstr) {} String(const String &str) : std::string(str) {} explicit String(long); + explicit String(int); explicit String(double, unsigned char decimalPlaces = 2); }; diff --git a/include/ArduinoJson/Internals/JsonFloat.hpp b/include/ArduinoJson/Internals/JsonFloat.hpp new file mode 100644 index 00000000..00622889 --- /dev/null +++ b/include/ArduinoJson/Internals/JsonFloat.hpp @@ -0,0 +1,20 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +#ifdef ARDUINO +// On embedded platform, we with use float instead of double to keep JsonVariant +// small (issue #134) +typedef float JsonFloat; +#else +typedef double JsonFloat; +#endif +} +} diff --git a/include/ArduinoJson/Internals/JsonInteger.hpp b/include/ArduinoJson/Internals/JsonInteger.hpp new file mode 100644 index 00000000..18c11185 --- /dev/null +++ b/include/ArduinoJson/Internals/JsonInteger.hpp @@ -0,0 +1,13 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +namespace ArduinoJson { +namespace Internals { +typedef long JsonInteger; +} +} diff --git a/include/ArduinoJson/Internals/JsonVariantContent.hpp b/include/ArduinoJson/Internals/JsonVariantContent.hpp index 05cbf1e3..48a195b9 100644 --- a/include/ArduinoJson/Internals/JsonVariantContent.hpp +++ b/include/ArduinoJson/Internals/JsonVariantContent.hpp @@ -6,6 +6,9 @@ #pragma once +#include "JsonFloat.hpp" +#include "JsonInteger.hpp" + namespace ArduinoJson { // Forward declarations @@ -13,15 +16,14 @@ class JsonArray; class JsonObject; namespace Internals { - // A union that defines the actual content of a JsonVariant. // The enum JsonVariantType determines which member is in use. union JsonVariantContent { - double asDouble; // asDouble is also used for float - long asLong; // asLong is also used for bool, char, short and int - 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 + 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 }; } } diff --git a/include/ArduinoJson/Internals/JsonVariantType.hpp b/include/ArduinoJson/Internals/JsonVariantType.hpp index 600e65db..8650c243 100644 --- a/include/ArduinoJson/Internals/JsonVariantType.hpp +++ b/include/ArduinoJson/Internals/JsonVariantType.hpp @@ -19,17 +19,17 @@ enum JsonVariantType { JSON_UNPARSED, // the JsonVariant contains an unparsed string JSON_STRING, // the JsonVariant stores a const char* JSON_BOOLEAN, // the JsonVariant stores a bool - JSON_LONG, // the JsonVariant stores a long + 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 - // The following values are reserved for double values + // The following values are reserved for float values // Multiple values are used for double, depending on the number of decimal // digits that must be printed in the JSON output. // This little trick allow to save one extra member in JsonVariant - JSON_DOUBLE_0_DECIMALS - // JSON_DOUBLE_1_DECIMAL - // JSON_DOUBLE_2_DECIMALS + JSON_FLOAT_0_DECIMALS + // JSON_FLOAT_1_DECIMAL + // JSON_FLOAT_2_DECIMALS // ... }; } diff --git a/include/ArduinoJson/Internals/JsonWriter.hpp b/include/ArduinoJson/Internals/JsonWriter.hpp index eda0039a..68df3670 100644 --- a/include/ArduinoJson/Internals/JsonWriter.hpp +++ b/include/ArduinoJson/Internals/JsonWriter.hpp @@ -9,6 +9,8 @@ #include "../Arduino/Print.hpp" #include "Encoding.hpp" #include "ForceInline.hpp" +#include "JsonFloat.hpp" +#include "JsonInteger.hpp" namespace ArduinoJson { namespace Internals { @@ -60,9 +62,9 @@ class JsonWriter { } } - void writeLong(long value) { _length += _sink.print(value); } + void writeInteger(JsonInteger value) { _length += _sink.print(value); } - void writeDouble(double value, uint8_t decimals) { + void writeFloat(JsonFloat value, uint8_t decimals) { _length += _sink.print(value, decimals); } diff --git a/include/ArduinoJson/Internals/StaticStringBuilder.hpp b/include/ArduinoJson/Internals/StaticStringBuilder.hpp index 86889941..f93a6414 100644 --- a/include/ArduinoJson/Internals/StaticStringBuilder.hpp +++ b/include/ArduinoJson/Internals/StaticStringBuilder.hpp @@ -14,7 +14,7 @@ namespace Internals { // A Print implementation that allows to write in a char[] class StaticStringBuilder : public Print { public: - StaticStringBuilder(char *buf, int size) + StaticStringBuilder(char *buf, size_t size) : buffer(buf), capacity(size - 1), length(0) { buffer[0] = '\0'; } @@ -23,8 +23,8 @@ class StaticStringBuilder : public Print { private: char *buffer; - int capacity; - int length; + size_t capacity; + size_t length; }; } } diff --git a/include/ArduinoJson/JsonVariant.hpp b/include/ArduinoJson/JsonVariant.hpp index 914a4c74..97997248 100644 --- a/include/ArduinoJson/JsonVariant.hpp +++ b/include/ArduinoJson/JsonVariant.hpp @@ -83,6 +83,9 @@ class JsonVariant : public JsonVariantBase { static T invalid(); private: + Internals::JsonFloat asFloat() const; + Internals::JsonInteger asInteger() const; + // The current type of the variant Internals::JsonVariantType _type; diff --git a/include/ArduinoJson/JsonVariant.ipp b/include/ArduinoJson/JsonVariant.ipp index dc40965b..c1b2a7f2 100644 --- a/include/ArduinoJson/JsonVariant.ipp +++ b/include/ArduinoJson/JsonVariant.ipp @@ -11,8 +11,9 @@ namespace ArduinoJson { inline JsonVariant::JsonVariant(bool value) { - _type = Internals::JSON_BOOLEAN; - _content.asLong = value; + using namespace Internals; + _type = JSON_BOOLEAN; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(const char *value) { @@ -26,15 +27,15 @@ inline JsonVariant::JsonVariant(Internals::Unparsed value) { } inline JsonVariant::JsonVariant(double value, uint8_t decimals) { - _type = static_cast( - Internals::JSON_DOUBLE_0_DECIMALS + decimals); - _content.asDouble = value; + using namespace Internals; + _type = static_cast(JSON_FLOAT_0_DECIMALS + decimals); + _content.asFloat = static_cast(value); } inline JsonVariant::JsonVariant(float value, uint8_t decimals) { - _type = static_cast( - Internals::JSON_DOUBLE_0_DECIMALS + decimals); - _content.asDouble = value; + using namespace Internals; + _type = static_cast(JSON_FLOAT_0_DECIMALS + decimals); + _content.asFloat = static_cast(value); } inline JsonVariant::JsonVariant(JsonArray &array) { @@ -48,51 +49,53 @@ inline JsonVariant::JsonVariant(JsonObject &object) { } inline JsonVariant::JsonVariant(signed char value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(signed int value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(signed long value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(signed short value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(unsigned char value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(unsigned int value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(unsigned long value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } inline JsonVariant::JsonVariant(unsigned short value) { - _type = Internals::JSON_LONG; - _content.asLong = value; + using namespace Internals; + _type = JSON_INTEGER; + _content.asInteger = static_cast(value); } -template <> -double JsonVariant::as() const; - -template <> -long JsonVariant::as() const; - template <> String JsonVariant::as() const; @@ -101,47 +104,57 @@ const char *JsonVariant::as() const; template <> inline bool JsonVariant::as() const { - return as() != 0; + return asInteger() != 0; } template <> inline signed char JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline unsigned char JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline signed short JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline unsigned short JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline signed int JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline unsigned int JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); } template <> inline unsigned long JsonVariant::as() const { - return static_cast(as()); + return static_cast(asInteger()); +} + +template <> +inline signed long JsonVariant::as() const { + return static_cast(asInteger()); +} + +template <> +inline double JsonVariant::as() const { + return static_cast(asFloat()); } template <> inline float JsonVariant::as() const { - return static_cast(as()); + return static_cast(asFloat()); } template diff --git a/src/Arduino/Print.cpp b/src/Arduino/Print.cpp index 5b443ac0..b6a19674 100644 --- a/src/Arduino/Print.cpp +++ b/src/Arduino/Print.cpp @@ -60,6 +60,12 @@ size_t Print::print(long value) { return print(tmp); } +size_t Print::print(int value) { + char tmp[32]; + sprintf(tmp, "%d", value); + return print(tmp); +} + size_t Print::println() { return write('\r') + write('\n'); } #endif diff --git a/src/Arduino/String.cpp b/src/Arduino/String.cpp index b2a1b59f..def11c6f 100644 --- a/src/Arduino/String.cpp +++ b/src/Arduino/String.cpp @@ -22,4 +22,10 @@ String::String(long value) { *this = tmp; } +String::String(int value) { + char tmp[32]; + sprintf(tmp, "%d", value); + *this = tmp; +} + #endif diff --git a/src/JsonVariant.cpp b/src/JsonVariant.cpp index 52f609c9..5066ebd8 100644 --- a/src/JsonVariant.cpp +++ b/src/JsonVariant.cpp @@ -16,6 +16,29 @@ using namespace ArduinoJson::Internals; namespace ArduinoJson { +template +static TFloat parse(const char *); + +template <> +FORCE_INLINE float parse(const char *s) { + return static_cast(strtod(s, NULL)); +} + +template <> +FORCE_INLINE double parse(const char *s) { + return strtod(s, NULL); +} + +template <> +FORCE_INLINE long parse(const char *s) { + return strtol(s, NULL, 10); +} + +template <> +FORCE_INLINE int parse(const char *s) { + return atoi(s); +} + template <> const char *JsonVariant::as() const { if (_type == JSON_UNPARSED && _content.asString && @@ -25,29 +48,27 @@ const char *JsonVariant::as() const { return NULL; } -template <> -double JsonVariant::as() const { - if (_type >= JSON_DOUBLE_0_DECIMALS) return _content.asDouble; +JsonFloat JsonVariant::asFloat() const { + if (_type >= JSON_FLOAT_0_DECIMALS) return _content.asFloat; - if (_type == JSON_LONG || _type == JSON_BOOLEAN) - return static_cast(_content.asLong); + if (_type == JSON_INTEGER || _type == JSON_BOOLEAN) + return static_cast(_content.asInteger); if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) - return strtod(_content.asString, NULL); + return parse(_content.asString); return 0.0; } -template <> -long JsonVariant::as() const { - if (_type == JSON_LONG || _type == JSON_BOOLEAN) return _content.asLong; +JsonInteger JsonVariant::asInteger() const { + if (_type == JSON_INTEGER || _type == JSON_BOOLEAN) return _content.asInteger; - if (_type >= JSON_DOUBLE_0_DECIMALS) - return static_cast(_content.asDouble); + if (_type >= JSON_FLOAT_0_DECIMALS) + return static_cast(_content.asFloat); if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) { if (!strcmp("true", _content.asString)) return 1; - return strtol(_content.asString, NULL, 10); + return parse(_content.asString); } return 0L; @@ -59,12 +80,12 @@ String JsonVariant::as() const { _content.asString != NULL) return String(_content.asString); - if (_type == JSON_LONG || _type == JSON_BOOLEAN) - return String(_content.asLong); + if (_type == JSON_INTEGER || _type == JSON_BOOLEAN) + return String(_content.asInteger); - if (_type >= JSON_DOUBLE_0_DECIMALS) { - uint8_t decimals = static_cast(_type - JSON_DOUBLE_0_DECIMALS); - return String(_content.asDouble, decimals); + if (_type >= JSON_FLOAT_0_DECIMALS) { + uint8_t decimals = static_cast(_type - JSON_FLOAT_0_DECIMALS); + return String(_content.asFloat, decimals); } String s; @@ -74,7 +95,7 @@ String JsonVariant::as() const { template <> bool JsonVariant::is() const { - if (_type == JSON_LONG) return true; + if (_type == JSON_INTEGER) return true; if (_type != JSON_UNPARSED || _content.asString == NULL) return false; @@ -87,7 +108,7 @@ bool JsonVariant::is() const { template <> bool JsonVariant::is() const { - if (_type >= JSON_DOUBLE_0_DECIMALS) return true; + if (_type >= JSON_FLOAT_0_DECIMALS) return true; if (_type != JSON_UNPARSED || _content.asString == NULL) return false; @@ -111,15 +132,15 @@ void JsonVariant::writeTo(JsonWriter &writer) const { else if (_type == JSON_UNPARSED) writer.writeRaw(_content.asString); - else if (_type == JSON_LONG) - writer.writeLong(_content.asLong); + else if (_type == JSON_INTEGER) + writer.writeInteger(_content.asInteger); else if (_type == JSON_BOOLEAN) - writer.writeBoolean(_content.asLong != 0); + writer.writeBoolean(_content.asInteger != 0); - else if (_type >= JSON_DOUBLE_0_DECIMALS) { - uint8_t decimals = static_cast(_type - JSON_DOUBLE_0_DECIMALS); - writer.writeDouble(_content.asDouble, decimals); + else if (_type >= JSON_FLOAT_0_DECIMALS) { + uint8_t decimals = static_cast(_type - JSON_FLOAT_0_DECIMALS); + writer.writeFloat(_content.asFloat, decimals); } } }