Added custom implementation of strtod() (issue #453)

This commit is contained in:
Benoit Blanchon
2017-03-19 15:23:06 +01:00
parent 13409c433a
commit c4567bac18
15 changed files with 593 additions and 83 deletions

View File

@@ -1,66 +0,0 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include <stdlib.h>
namespace ArduinoJson {
namespace Internals {
template <typename TFloat>
TFloat parse(const char *);
template <>
inline float parse<float>(const char *s) {
return static_cast<float>(strtod(s, NULL));
}
template <>
inline double parse<double>(const char *s) {
return strtod(s, NULL);
}
template <>
inline long parse<long>(const char *s) {
return strtol(s, NULL, 10);
}
template <>
inline unsigned long parse<unsigned long>(const char *s) {
return strtoul(s, NULL, 10);
}
template <>
inline int parse<int>(const char *s) {
return atoi(s);
}
#if ARDUINOJSON_USE_LONG_LONG
template <>
inline long long parse<long long>(const char *s) {
return strtoll(s, NULL, 10);
}
template <>
inline unsigned long long parse<unsigned long long>(const char *s) {
return strtoull(s, NULL, 10);
}
#endif
#if ARDUINOJSON_USE_INT64
template <>
inline __int64 parse<__int64>(const char *s) {
return _strtoi64(s, NULL, 10);
}
template <>
inline unsigned __int64 parse<unsigned __int64>(const char *s) {
return _strtoui64(s, NULL, 10);
}
#endif
}
}

View File

@@ -8,10 +8,12 @@
#pragma once
#include "Configuration.hpp"
#include "Data/Parse.hpp"
#include "JsonArray.hpp"
#include "JsonObject.hpp"
#include "JsonVariant.hpp"
#include "Polyfills/isFloat.hpp"
#include "Polyfills/parseFloat.hpp"
#include "Polyfills/parseInteger.hpp"
#include <errno.h> // for errno
#include <stdlib.h> // for strtol, strtod
@@ -56,14 +58,14 @@ inline Internals::JsonInteger JsonVariant::variantAsInteger() const {
case JSON_BOOLEAN:
return _content.asInteger;
case JSON_NEGATIVE_INTEGER:
return -static_cast<Internals::JsonInteger>(_content.asInteger);
return -static_cast<JsonInteger>(_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);
return Polyfills::parseInteger<JsonInteger>(_content.asString);
default:
return static_cast<Internals::JsonInteger>(_content.asFloat);
return static_cast<JsonInteger>(_content.asFloat);
}
}
@@ -80,9 +82,9 @@ inline Internals::JsonUInt JsonVariant::asUnsignedInteger() const {
case JSON_UNPARSED:
if (!_content.asString) return 0;
if (!strcmp("true", _content.asString)) return 1;
return parse<Internals::JsonUInt>(_content.asString);
return Polyfills::parseInteger<JsonUInt>(_content.asString);
default:
return static_cast<Internals::JsonUInt>(_content.asFloat);
return static_cast<JsonUInt>(_content.asFloat);
}
}
@@ -107,7 +109,9 @@ inline Internals::JsonFloat JsonVariant::variantAsFloat() const {
return -static_cast<JsonFloat>(_content.asInteger);
case JSON_STRING:
case JSON_UNPARSED:
return _content.asString ? parse<JsonFloat>(_content.asString) : 0;
return _content.asString
? Polyfills::parseFloat<JsonFloat>(_content.asString)
: 0;
default:
return _content.asFloat;
}
@@ -143,11 +147,7 @@ inline bool JsonVariant::isFloat() const {
if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
char *end;
errno = 0;
strtod(_content.asString, &end);
return *end == '\0' && errno == 0 && !is<long>();
return Polyfills::isFloat(_content.asString);
}
#if ARDUINOJSON_ENABLE_STD_STREAM

View File

@@ -0,0 +1,21 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
namespace ArduinoJson {
namespace Polyfills {
inline bool isdigit(char c) {
return '0' <= c && c <= '9';
}
inline bool issign(char c) {
return '-' == c || c == '+';
}
}
}

View File

@@ -0,0 +1,40 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "./ctype.hpp"
#include "./math.hpp"
namespace ArduinoJson {
namespace Polyfills {
inline bool isFloat(const char* s) {
if (!strcmp(s, "NaN")) return true;
if (issign(*s)) s++;
if (!strcmp(s, "Infinity")) return true;
while (isdigit(*s)) s++;
bool has_dot = *s == '.';
if (has_dot) {
s++;
while (isdigit(*s)) s++;
}
bool has_exponent = *s == 'e' || *s == 'E';
if (has_exponent) {
s++;
if (issign(*s)) s++;
if (!isdigit(*s)) return false;
while (isdigit(*s)) s++;
}
return (has_dot || has_exponent) && *s == '\0';
}
}
}

View File

@@ -7,10 +7,11 @@
#pragma once
// If Visual Studo <= 2012
#if defined(_MSC_VER) && _MSC_VER <= 1700
// If Visual Studo
#if defined(_MSC_VER)
#include <float.h>
#include <limits>
namespace ArduinoJson {
namespace Polyfills {
@@ -23,6 +24,16 @@ template <typename T>
bool isInfinity(T x) {
return !_finite(x);
}
template <typename T>
T nan() {
return std::numeric_limits<T>::quiet_NaN();
}
template <typename T>
T inf() {
return std::numeric_limits<T>::infinity();
}
}
}
@@ -101,6 +112,16 @@ inline bool isInfinity<float>(float x) {
}
#endif
template <typename T>
T nan() {
return static_cast<T>(NAN);
}
template <typename T>
T inf() {
return static_cast<T>(INFINITY);
}
#if defined(__GNUC__)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic pop

View File

@@ -0,0 +1,87 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "../TypeTraits/FloatTraits.hpp"
#include "./ctype.hpp"
#include "./math.hpp"
namespace ArduinoJson {
namespace Polyfills {
template <typename T>
inline T parseFloat(const char* s) {
typedef TypeTraits::FloatTraits<T> traits;
typedef typename traits::mantissa_type mantissa_t;
typedef typename traits::exponent_type exponent_t;
bool negative_result = false;
switch (*s) {
case '-':
negative_result = true;
case '+':
s++;
}
if (*s == 'n' || *s == 'N') return traits::nan();
if (*s == 'i' || *s == 'I')
return negative_result ? -traits::inf() : traits::inf();
mantissa_t mantissa = 0;
exponent_t exponent_offset = 0;
while (isdigit(*s)) {
if (mantissa < traits::mantissa_max / 10)
mantissa = mantissa * 10 + (*s - '0');
else
exponent_offset++;
s++;
}
if (*s == '.') {
s++;
while (isdigit(*s)) {
if (mantissa < traits::mantissa_max / 10) {
mantissa = mantissa * 10 + (*s - '0');
exponent_offset--;
}
s++;
}
}
int exponent = 0;
if (*s == 'e' || *s == 'E') {
s++;
bool negative_exponent = false;
if (*s == '-') {
negative_exponent = true;
s++;
} else if (*s == '+') {
s++;
}
while (isdigit(*s)) {
exponent = exponent * 10 + (*s - '0');
if (exponent + exponent_offset > traits::exponent_max) {
if (negative_exponent)
return negative_result ? -0.0f : 0.0f;
else
return negative_result ? -traits::inf() : traits::inf();
}
s++;
}
if (negative_exponent) exponent = -exponent;
}
exponent += exponent_offset;
T result = traits::make_float(static_cast<T>(mantissa), exponent);
return negative_result ? -result : result;
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include <stdlib.h>
namespace ArduinoJson {
namespace Polyfills {
template <typename T>
T parseInteger(const char *s);
template <>
inline long parseInteger<long>(const char *s) {
return ::strtol(s, NULL, 10);
}
template <>
inline unsigned long parseInteger<unsigned long>(const char *s) {
return ::strtoul(s, NULL, 10);
}
template <>
inline int parseInteger<int>(const char *s) {
return ::atoi(s);
}
#if ARDUINOJSON_USE_LONG_LONG
template <>
inline long long parseInteger<long long>(const char *s) {
return ::strtoll(s, NULL, 10);
}
template <>
inline unsigned long long parseInteger<unsigned long long>(const char *s) {
return ::strtoull(s, NULL, 10);
}
#endif
#if ARDUINOJSON_USE_INT64
template <>
inline __int64 parseInteger<__int64>(const char *s) {
return ::_strtoi64(s, NULL, 10);
}
template <>
inline unsigned __int64 parseInteger<unsigned __int64>(const char *s) {
return ::_strtoui64(s, NULL, 10);
}
#endif
}
}

View File

@@ -0,0 +1,83 @@
// Copyright Benoit Blanchon 2014-2017
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include <stdint.h>
#include "../Polyfills/math.hpp"
namespace ArduinoJson {
namespace TypeTraits {
template <typename T, size_t = sizeof(T)>
struct FloatTraits {};
#ifndef ARDUINO_ARCH_AVR // double is 32 bits, so 1e64 gives a warning
template <typename T>
struct FloatTraits<T, 8 /*64bits*/> {
typedef int64_t mantissa_type;
static const short mantissa_bits = 52;
static const mantissa_type mantissa_max =
(static_cast<mantissa_type>(1) << mantissa_bits) - 1;
typedef int16_t exponent_type;
static const exponent_type exponent_max = 308;
template <typename TExponent>
static T make_float(T m, TExponent e) {
if (e >= 0)
return m * (e & 1 ? 1e1 : 1) * (e & 2 ? 1e2 : 1) * (e & 4 ? 1e4 : 1) *
(e & 8 ? 1e8 : 1) * (e & 16 ? 1e16 : 1) * (e & 32 ? 1e32 : 1) *
(e & 64 ? 1e64 : 1) * (e & 128 ? 1e128 : 1) *
(e & 256 ? 1e256 : 1);
e = -e;
return m * (e & 1 ? 1e-1 : 1) * (e & 2 ? 1e-2 : 1) * (e & 4 ? 1e-4 : 1) *
(e & 8 ? 1e-8 : 1) * (e & 16 ? 1e-16 : 1) * (e & 32 ? 1e-32 : 1) *
(e & 64 ? 1e-64 : 1) * (e & 128 ? 1e-128 : 1) *
(e & 256 ? 1e-256 : 1);
}
static T nan() {
return Polyfills::nan<T>();
}
static T inf() {
return Polyfills::inf<T>();
}
};
#endif
template <typename T>
struct FloatTraits<T, 4 /*32bits*/> {
typedef int32_t mantissa_type;
static const short mantissa_bits = 23;
static const mantissa_type mantissa_max =
(static_cast<mantissa_type>(1) << mantissa_bits) - 1;
typedef int8_t exponent_type;
static const exponent_type exponent_max = 38;
template <typename TExponent>
static T make_float(T m, TExponent e) {
if (e > 0)
return m * (e & 1 ? 1e1f : 1) * (e & 2 ? 1e2f : 1) * (e & 4 ? 1e4f : 1) *
(e & 8 ? 1e8f : 1) * (e & 16 ? 1e16f : 1) * (e & 32 ? 1e32f : 1);
e = -e;
return m * (e & 1 ? 1e-1f : 1) * (e & 2 ? 1e-2f : 1) * (e & 4 ? 1e-4f : 1) *
(e & 8 ? 1e-8f : 1) * (e & 16 ? 1e-16f : 1) * (e & 32 ? 1e-32f : 1);
}
static T nan() {
return Polyfills::nan<T>();
}
static T inf() {
return Polyfills::inf<T>();
}
};
}
}