Use float instead of double to reduce the size of JsonVariant (issue #134)

This commit is contained in:
Benoit Blanchon
2015-10-30 23:03:16 +01:00
parent 9f3ce18f06
commit c0cf9c3fcc
15 changed files with 170 additions and 81 deletions

View File

@ -5,6 +5,7 @@ v5.0.5
------ ------
* Add overload `JsonObjectSuscript::set(value, decimals)` (issue #143) * Add overload `JsonObjectSuscript::set(value, decimals)` (issue #143)
* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134)
v5.0.4 v5.0.4
------ ------

View File

@ -63,7 +63,7 @@ data.add(2.302038, 6); // if not specified, 2 digits are printed
root.printTo(Serial); root.printTo(Serial);
// This prints: // This prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
`` ```
Documentation Documentation

View File

@ -20,6 +20,7 @@ class Print {
size_t print(const char[]); size_t print(const char[]);
size_t print(double, int = 2); size_t print(double, int = 2);
size_t print(int);
size_t print(long); size_t print(long);
size_t println(); size_t println();
}; };

View File

@ -16,6 +16,7 @@ class String : public std::string {
String(const char *cstr = "") : std::string(cstr) {} String(const char *cstr = "") : std::string(cstr) {}
String(const String &str) : std::string(str) {} String(const String &str) : std::string(str) {}
explicit String(long); explicit String(long);
explicit String(int);
explicit String(double, unsigned char decimalPlaces = 2); explicit String(double, unsigned char decimalPlaces = 2);
}; };

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -6,6 +6,9 @@
#pragma once #pragma once
#include "JsonFloat.hpp"
#include "JsonInteger.hpp"
namespace ArduinoJson { namespace ArduinoJson {
// Forward declarations // Forward declarations
@ -13,15 +16,14 @@ class JsonArray;
class JsonObject; class JsonObject;
namespace Internals { 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 {
double asDouble; // asDouble is also used for float JsonFloat asFloat; // used for double and float
long asLong; // asLong is also used for bool, char, short and int JsonInteger 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

@ -19,17 +19,17 @@ enum JsonVariantType {
JSON_UNPARSED, // the JsonVariant contains an unparsed string JSON_UNPARSED, // the JsonVariant contains an unparsed string
JSON_STRING, // the JsonVariant stores a const char* JSON_STRING, // the JsonVariant stores a const char*
JSON_BOOLEAN, // the JsonVariant stores a bool 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_ARRAY, // the JsonVariant stores a pointer to a JsonArray
JSON_OBJECT, // the JsonVariant stores a pointer to a JsonObject 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 // Multiple values are used for double, depending on the number of decimal
// digits that must be printed in the JSON output. // digits that must be printed in the JSON output.
// This little trick allow to save one extra member in JsonVariant // This little trick allow to save one extra member in JsonVariant
JSON_DOUBLE_0_DECIMALS JSON_FLOAT_0_DECIMALS
// JSON_DOUBLE_1_DECIMAL // JSON_FLOAT_1_DECIMAL
// JSON_DOUBLE_2_DECIMALS // JSON_FLOAT_2_DECIMALS
// ... // ...
}; };
} }

View File

@ -9,6 +9,8 @@
#include "../Arduino/Print.hpp" #include "../Arduino/Print.hpp"
#include "Encoding.hpp" #include "Encoding.hpp"
#include "ForceInline.hpp" #include "ForceInline.hpp"
#include "JsonFloat.hpp"
#include "JsonInteger.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { 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); _length += _sink.print(value, decimals);
} }

View File

@ -14,7 +14,7 @@ namespace Internals {
// A Print implementation that allows to write in a char[] // A Print implementation that allows to write in a char[]
class StaticStringBuilder : public Print { class StaticStringBuilder : public Print {
public: public:
StaticStringBuilder(char *buf, int size) StaticStringBuilder(char *buf, size_t size)
: buffer(buf), capacity(size - 1), length(0) { : buffer(buf), capacity(size - 1), length(0) {
buffer[0] = '\0'; buffer[0] = '\0';
} }
@ -23,8 +23,8 @@ class StaticStringBuilder : public Print {
private: private:
char *buffer; char *buffer;
int capacity; size_t capacity;
int length; size_t length;
}; };
} }
} }

View File

@ -83,6 +83,9 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
static T invalid(); static T invalid();
private: private:
Internals::JsonFloat asFloat() const;
Internals::JsonInteger asInteger() const;
// The current type of the variant // The current type of the variant
Internals::JsonVariantType _type; Internals::JsonVariantType _type;

View File

@ -11,8 +11,9 @@
namespace ArduinoJson { namespace ArduinoJson {
inline JsonVariant::JsonVariant(bool value) { inline JsonVariant::JsonVariant(bool value) {
_type = Internals::JSON_BOOLEAN; using namespace Internals;
_content.asLong = value; _type = JSON_BOOLEAN;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(const char *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) { inline JsonVariant::JsonVariant(double value, uint8_t decimals) {
_type = static_cast<Internals::JsonVariantType>( using namespace Internals;
Internals::JSON_DOUBLE_0_DECIMALS + decimals); _type = static_cast<JsonVariantType>(JSON_FLOAT_0_DECIMALS + decimals);
_content.asDouble = value; _content.asFloat = static_cast<JsonFloat>(value);
} }
inline JsonVariant::JsonVariant(float value, uint8_t decimals) { inline JsonVariant::JsonVariant(float value, uint8_t decimals) {
_type = static_cast<Internals::JsonVariantType>( using namespace Internals;
Internals::JSON_DOUBLE_0_DECIMALS + decimals); _type = static_cast<JsonVariantType>(JSON_FLOAT_0_DECIMALS + decimals);
_content.asDouble = value; _content.asFloat = static_cast<JsonFloat>(value);
} }
inline JsonVariant::JsonVariant(JsonArray &array) { inline JsonVariant::JsonVariant(JsonArray &array) {
@ -48,51 +49,53 @@ inline JsonVariant::JsonVariant(JsonObject &object) {
} }
inline JsonVariant::JsonVariant(signed char value) { inline JsonVariant::JsonVariant(signed char value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(signed int value) { inline JsonVariant::JsonVariant(signed int value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(signed long value) { inline JsonVariant::JsonVariant(signed long value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(signed short value) { inline JsonVariant::JsonVariant(signed short value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(unsigned char value) { inline JsonVariant::JsonVariant(unsigned char value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(unsigned int value) { inline JsonVariant::JsonVariant(unsigned int value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(unsigned long value) { inline JsonVariant::JsonVariant(unsigned long value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
inline JsonVariant::JsonVariant(unsigned short value) { inline JsonVariant::JsonVariant(unsigned short value) {
_type = Internals::JSON_LONG; using namespace Internals;
_content.asLong = value; _type = JSON_INTEGER;
_content.asInteger = static_cast<JsonInteger>(value);
} }
template <>
double JsonVariant::as<double>() const;
template <>
long JsonVariant::as<long>() const;
template <> template <>
String JsonVariant::as<String>() const; String JsonVariant::as<String>() const;
@ -101,47 +104,57 @@ const char *JsonVariant::as<const char *>() const;
template <> template <>
inline bool JsonVariant::as<bool>() const { inline bool JsonVariant::as<bool>() const {
return as<long>() != 0; return asInteger() != 0;
} }
template <> template <>
inline signed char JsonVariant::as<signed char>() const { inline signed char JsonVariant::as<signed char>() const {
return static_cast<signed char>(as<long>()); return static_cast<signed char>(asInteger());
} }
template <> template <>
inline unsigned char JsonVariant::as<unsigned char>() const { inline unsigned char JsonVariant::as<unsigned char>() const {
return static_cast<unsigned char>(as<long>()); return static_cast<unsigned char>(asInteger());
} }
template <> template <>
inline signed short JsonVariant::as<signed short>() const { inline signed short JsonVariant::as<signed short>() const {
return static_cast<signed short>(as<long>()); return static_cast<signed short>(asInteger());
} }
template <> template <>
inline unsigned short JsonVariant::as<unsigned short>() const { inline unsigned short JsonVariant::as<unsigned short>() const {
return static_cast<unsigned short>(as<long>()); return static_cast<unsigned short>(asInteger());
} }
template <> template <>
inline signed int JsonVariant::as<signed int>() const { inline signed int JsonVariant::as<signed int>() const {
return static_cast<signed int>(as<long>()); return static_cast<signed int>(asInteger());
} }
template <> template <>
inline unsigned int JsonVariant::as<unsigned int>() const { inline unsigned int JsonVariant::as<unsigned int>() const {
return static_cast<unsigned int>(as<long>()); return static_cast<unsigned int>(asInteger());
} }
template <> template <>
inline unsigned long JsonVariant::as<unsigned long>() const { inline unsigned long JsonVariant::as<unsigned long>() const {
return static_cast<unsigned long>(as<long>()); return static_cast<unsigned long>(asInteger());
}
template <>
inline signed long JsonVariant::as<signed long>() const {
return static_cast<unsigned long>(asInteger());
}
template <>
inline double JsonVariant::as<double>() const {
return static_cast<double>(asFloat());
} }
template <> template <>
inline float JsonVariant::as<float>() const { inline float JsonVariant::as<float>() const {
return static_cast<float>(as<double>()); return static_cast<float>(asFloat());
} }
template <typename T> template <typename T>

View File

@ -60,6 +60,12 @@ size_t Print::print(long value) {
return print(tmp); 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'); } size_t Print::println() { return write('\r') + write('\n'); }
#endif #endif

View File

@ -22,4 +22,10 @@ String::String(long value) {
*this = tmp; *this = tmp;
} }
String::String(int value) {
char tmp[32];
sprintf(tmp, "%d", value);
*this = tmp;
}
#endif #endif

View File

@ -16,6 +16,29 @@ using namespace ArduinoJson::Internals;
namespace ArduinoJson { namespace ArduinoJson {
template <typename TFloat>
static TFloat parse(const char *);
template <>
FORCE_INLINE float parse<float>(const char *s) {
return static_cast<float>(strtod(s, NULL));
}
template <>
FORCE_INLINE double parse<double>(const char *s) {
return strtod(s, NULL);
}
template <>
FORCE_INLINE long parse<long>(const char *s) {
return strtol(s, NULL, 10);
}
template <>
FORCE_INLINE int parse<int>(const char *s) {
return atoi(s);
}
template <> template <>
const char *JsonVariant::as<const char *>() const { const char *JsonVariant::as<const char *>() const {
if (_type == JSON_UNPARSED && _content.asString && if (_type == JSON_UNPARSED && _content.asString &&
@ -25,29 +48,27 @@ const char *JsonVariant::as<const char *>() const {
return NULL; return NULL;
} }
template <> JsonFloat JsonVariant::asFloat() const {
double JsonVariant::as<double>() const { if (_type >= JSON_FLOAT_0_DECIMALS) return _content.asFloat;
if (_type >= JSON_DOUBLE_0_DECIMALS) return _content.asDouble;
if (_type == JSON_LONG || _type == JSON_BOOLEAN) if (_type == JSON_INTEGER || _type == JSON_BOOLEAN)
return static_cast<double>(_content.asLong); return static_cast<JsonFloat>(_content.asInteger);
if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString)
return strtod(_content.asString, NULL); return parse<JsonFloat>(_content.asString);
return 0.0; return 0.0;
} }
template <> JsonInteger JsonVariant::asInteger() const {
long JsonVariant::as<long>() const { if (_type == JSON_INTEGER || _type == JSON_BOOLEAN) return _content.asInteger;
if (_type == JSON_LONG || _type == JSON_BOOLEAN) return _content.asLong;
if (_type >= JSON_DOUBLE_0_DECIMALS) if (_type >= JSON_FLOAT_0_DECIMALS)
return static_cast<long>(_content.asDouble); return static_cast<JsonInteger>(_content.asFloat);
if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) { if ((_type == JSON_STRING || _type == JSON_UNPARSED) && _content.asString) {
if (!strcmp("true", _content.asString)) return 1; if (!strcmp("true", _content.asString)) return 1;
return strtol(_content.asString, NULL, 10); return parse<JsonInteger>(_content.asString);
} }
return 0L; return 0L;
@ -59,12 +80,12 @@ String JsonVariant::as<String>() const {
_content.asString != NULL) _content.asString != NULL)
return String(_content.asString); return String(_content.asString);
if (_type == JSON_LONG || _type == JSON_BOOLEAN) if (_type == JSON_INTEGER || _type == JSON_BOOLEAN)
return String(_content.asLong); return String(_content.asInteger);
if (_type >= JSON_DOUBLE_0_DECIMALS) { if (_type >= JSON_FLOAT_0_DECIMALS) {
uint8_t decimals = static_cast<uint8_t>(_type - JSON_DOUBLE_0_DECIMALS); uint8_t decimals = static_cast<uint8_t>(_type - JSON_FLOAT_0_DECIMALS);
return String(_content.asDouble, decimals); return String(_content.asFloat, decimals);
} }
String s; String s;
@ -74,7 +95,7 @@ String JsonVariant::as<String>() const {
template <> template <>
bool JsonVariant::is<signed long>() const { bool JsonVariant::is<signed long>() const {
if (_type == JSON_LONG) return true; if (_type == JSON_INTEGER) return true;
if (_type != JSON_UNPARSED || _content.asString == NULL) return false; if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
@ -87,7 +108,7 @@ bool JsonVariant::is<signed long>() const {
template <> template <>
bool JsonVariant::is<double>() const { bool JsonVariant::is<double>() 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; if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
@ -111,15 +132,15 @@ void JsonVariant::writeTo(JsonWriter &writer) const {
else if (_type == JSON_UNPARSED) else if (_type == JSON_UNPARSED)
writer.writeRaw(_content.asString); writer.writeRaw(_content.asString);
else if (_type == JSON_LONG) else if (_type == JSON_INTEGER)
writer.writeLong(_content.asLong); writer.writeInteger(_content.asInteger);
else if (_type == JSON_BOOLEAN) else if (_type == JSON_BOOLEAN)
writer.writeBoolean(_content.asLong != 0); writer.writeBoolean(_content.asInteger != 0);
else if (_type >= JSON_DOUBLE_0_DECIMALS) { else if (_type >= JSON_FLOAT_0_DECIMALS) {
uint8_t decimals = static_cast<uint8_t>(_type - JSON_DOUBLE_0_DECIMALS); uint8_t decimals = static_cast<uint8_t>(_type - JSON_FLOAT_0_DECIMALS);
writer.writeDouble(_content.asDouble, decimals); writer.writeFloat(_content.asFloat, decimals);
} }
} }
} }