summaryrefslogtreecommitdiff
path: root/include/lib/ArduinoJson/Numbers/parseNumber.hpp
blob: 54395ead93687c37a7a1ef741dc7eb45da835c19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// ArduinoJson - https://arduinojson.org
// Copyright Benoit Blanchon 2014-2021
// MIT License

#pragma once

#include "lib/ArduinoJson/Numbers/FloatTraits.hpp"
#include "lib/ArduinoJson/Numbers/convertNumber.hpp"
#include "lib/ArduinoJson/Polyfills/assert.hpp"
#include "lib/ArduinoJson/Polyfills/ctype.hpp"
#include "lib/ArduinoJson/Polyfills/math.hpp"
#include "lib/ArduinoJson/Polyfills/type_traits.hpp"
#include "lib/ArduinoJson/Variant/Converter.hpp"
#include "lib/ArduinoJson/Variant/VariantData.hpp"

namespace ARDUINOJSON_NAMESPACE {

template <typename A, typename B>
struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {};

inline bool parseNumber(const char* s, VariantData& result) {
  typedef FloatTraits<Float> traits;
  typedef choose_largest<traits::mantissa_type, UInt>::type mantissa_t;
  typedef traits::exponent_type exponent_t;

  ARDUINOJSON_ASSERT(s != 0);

  bool is_negative = false;
  switch (*s) {
    case '-':
      is_negative = true;
      s++;
      break;
    case '+':
      s++;
      break;
  }

#if ARDUINOJSON_ENABLE_NAN
  if (*s == 'n' || *s == 'N') {
    result.setFloat(traits::nan());
    return true;
  }
#endif

#if ARDUINOJSON_ENABLE_INFINITY
  if (*s == 'i' || *s == 'I') {
    result.setFloat(is_negative ? -traits::inf() : traits::inf());
    return true;
  }
#endif

  if (!isdigit(*s) && *s != '.')
    return false;

  mantissa_t mantissa = 0;
  exponent_t exponent_offset = 0;
  const mantissa_t maxUint = UInt(-1);

  while (isdigit(*s)) {
    uint8_t digit = uint8_t(*s - '0');
    if (mantissa > maxUint / 10)
      break;
    mantissa *= 10;
    if (mantissa > maxUint - digit)
      break;
    mantissa += digit;
    s++;
  }

  if (*s == '\0') {
    if (is_negative) {
      const mantissa_t sintMantissaMax = mantissa_t(1)
                                         << (sizeof(Integer) * 8 - 1);
      if (mantissa <= sintMantissaMax) {
        result.setInteger(Integer(~mantissa + 1));
        return true;
      }
    } else {
      result.setInteger(UInt(mantissa));
      return true;
    }
  }

  // avoid mantissa overflow
  while (mantissa > traits::mantissa_max) {
    mantissa /= 10;
    exponent_offset++;
  }

  // remaing digits can't fit in the mantissa
  while (isdigit(*s)) {
    exponent_offset++;
    s++;
  }

  if (*s == '.') {
    s++;
    while (isdigit(*s)) {
      if (mantissa < traits::mantissa_max / 10) {
        mantissa = mantissa * 10 + uint8_t(*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)
          result.setFloat(is_negative ? -0.0f : 0.0f);
        else
          result.setFloat(is_negative ? -traits::inf() : traits::inf());
        return true;
      }
      s++;
    }
    if (negative_exponent)
      exponent = -exponent;
  }
  exponent += exponent_offset;

  // we should be at the end of the string, otherwise it's an error
  if (*s != '\0')
    return false;

  Float final_result =
      traits::make_float(static_cast<Float>(mantissa), exponent);

  result.setFloat(is_negative ? -final_result : final_result);
  return true;
}

template <typename T>
inline T parseNumber(const char* s) {
  VariantData value;
  value.init();  // VariantData is a POD, so it has no constructor
  parseNumber(s, value);
  return Converter<T>::fromJson(VariantConstRef(&value));
}
}  // namespace ARDUINOJSON_NAMESPACE