Minh Nguyen / ArduinoJson
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers parseNumber.hpp Source File

parseNumber.hpp

00001 // ArduinoJson - arduinojson.org
00002 // Copyright Benoit Blanchon 2014-2021
00003 // MIT License
00004 
00005 #pragma once
00006 
00007 #include <ArduinoJson/Numbers/FloatTraits.hpp>
00008 #include <ArduinoJson/Numbers/convertNumber.hpp>
00009 #include <ArduinoJson/Polyfills/assert.hpp>
00010 #include <ArduinoJson/Polyfills/ctype.hpp>
00011 #include <ArduinoJson/Polyfills/math.hpp>
00012 #include <ArduinoJson/Polyfills/type_traits.hpp>
00013 #include <ArduinoJson/Variant/VariantAs.hpp>
00014 #include <ArduinoJson/Variant/VariantData.hpp>
00015 
00016 namespace ARDUINOJSON_NAMESPACE {
00017 
00018 template <typename A, typename B>
00019 struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {};
00020 
00021 inline bool parseNumber(const char* s, VariantData& result) {
00022   typedef FloatTraits<Float> traits;
00023   typedef choose_largest<traits::mantissa_type, UInt>::type mantissa_t;
00024   typedef traits::exponent_type exponent_t;
00025 
00026   ARDUINOJSON_ASSERT(s != 0);
00027 
00028   bool is_negative = false;
00029   switch (*s) {
00030     case '-':
00031       is_negative = true;
00032       s++;
00033       break;
00034     case '+':
00035       s++;
00036       break;
00037   }
00038 
00039 #if ARDUINOJSON_ENABLE_NAN
00040   if (*s == 'n' || *s == 'N') {
00041     result.setFloat(traits::nan());
00042     return true;
00043   }
00044 #endif
00045 
00046 #if ARDUINOJSON_ENABLE_INFINITY
00047   if (*s == 'i' || *s == 'I') {
00048     result.setFloat(is_negative ? -traits::inf() : traits::inf());
00049     return true;
00050   }
00051 #endif
00052 
00053   if (!isdigit(*s) && *s != '.')
00054     return false;
00055 
00056   mantissa_t mantissa = 0;
00057   exponent_t exponent_offset = 0;
00058   const mantissa_t maxUint = UInt(-1);
00059 
00060   while (isdigit(*s)) {
00061     uint8_t digit = uint8_t(*s - '0');
00062     if (mantissa > maxUint / 10)
00063       break;
00064     mantissa *= 10;
00065     if (mantissa > maxUint - digit)
00066       break;
00067     mantissa += digit;
00068     s++;
00069   }
00070 
00071   if (*s == '\0') {
00072     if (is_negative)
00073       result.setNegativeInteger(UInt(mantissa));
00074     else
00075       result.setPositiveInteger(UInt(mantissa));
00076     return true;
00077   }
00078 
00079   // avoid mantissa overflow
00080   while (mantissa > traits::mantissa_max) {
00081     mantissa /= 10;
00082     exponent_offset++;
00083   }
00084 
00085   // remaing digits can't fit in the mantissa
00086   while (isdigit(*s)) {
00087     exponent_offset++;
00088     s++;
00089   }
00090 
00091   if (*s == '.') {
00092     s++;
00093     while (isdigit(*s)) {
00094       if (mantissa < traits::mantissa_max / 10) {
00095         mantissa = mantissa * 10 + uint8_t(*s - '0');
00096         exponent_offset--;
00097       }
00098       s++;
00099     }
00100   }
00101 
00102   int exponent = 0;
00103   if (*s == 'e' || *s == 'E') {
00104     s++;
00105     bool negative_exponent = false;
00106     if (*s == '-') {
00107       negative_exponent = true;
00108       s++;
00109     } else if (*s == '+') {
00110       s++;
00111     }
00112 
00113     while (isdigit(*s)) {
00114       exponent = exponent * 10 + (*s - '0');
00115       if (exponent + exponent_offset > traits::exponent_max) {
00116         if (negative_exponent)
00117           result.setFloat(is_negative ? -0.0f : 0.0f);
00118         else
00119           result.setFloat(is_negative ? -traits::inf() : traits::inf());
00120         return true;
00121       }
00122       s++;
00123     }
00124     if (negative_exponent)
00125       exponent = -exponent;
00126   }
00127   exponent += exponent_offset;
00128 
00129   // we should be at the end of the string, otherwise it's an error
00130   if (*s != '\0')
00131     return false;
00132 
00133   Float final_result =
00134       traits::make_float(static_cast<Float>(mantissa), exponent);
00135 
00136   result.setFloat(is_negative ? -final_result : final_result);
00137   return true;
00138 }
00139 
00140 template <typename T>
00141 inline T parseNumber(const char* s) {
00142   VariantData value;
00143   value.init();  // VariantData is a POD, so it has no constructor
00144   parseNumber(s, value);
00145   return variantAs<T>(&value);
00146 }
00147 }  // namespace ARDUINOJSON_NAMESPACE