Coverage Report

Created: 2026-01-10 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/arduinojson/src/ArduinoJson/Numbers/parseNumber.hpp
Line
Count
Source
1
// ArduinoJson - https://arduinojson.org
2
// Copyright © 2014-2025, Benoit BLANCHON
3
// MIT License
4
5
#pragma once
6
7
#include <ArduinoJson/Numbers/FloatTraits.hpp>
8
#include <ArduinoJson/Numbers/JsonFloat.hpp>
9
#include <ArduinoJson/Numbers/convertNumber.hpp>
10
#include <ArduinoJson/Polyfills/assert.hpp>
11
#include <ArduinoJson/Polyfills/ctype.hpp>
12
#include <ArduinoJson/Polyfills/math.hpp>
13
#include <ArduinoJson/Polyfills/type_traits.hpp>
14
15
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
16
17
template <typename A, typename B>
18
using largest_type = conditional_t<(sizeof(A) > sizeof(B)), A, B>;
19
20
enum class NumberType : uint8_t {
21
  Invalid,
22
  Float,
23
  SignedInteger,
24
  UnsignedInteger,
25
#if ARDUINOJSON_USE_DOUBLE
26
  Double,
27
#endif
28
};
29
30
union NumberValue {
31
169
  NumberValue() {}
32
3.94k
  NumberValue(float x) : asFloat(x) {}
33
634
  NumberValue(JsonInteger x) : asSignedInteger(x) {}
34
2.11k
  NumberValue(JsonUInt x) : asUnsignedInteger(x) {}
35
#if ARDUINOJSON_USE_DOUBLE
36
1.22k
  NumberValue(double x) : asDouble(x) {}
37
#endif
38
39
  JsonInteger asSignedInteger;
40
  JsonUInt asUnsignedInteger;
41
  float asFloat;
42
#if ARDUINOJSON_USE_DOUBLE
43
  double asDouble;
44
#endif
45
};
46
47
class Number {
48
  NumberType type_;
49
  NumberValue value_;
50
51
 public:
52
169
  Number() : type_(NumberType::Invalid) {}
53
3.94k
  Number(float value) : type_(NumberType::Float), value_(value) {}
54
634
  Number(JsonInteger value) : type_(NumberType::SignedInteger), value_(value) {}
55
2.11k
  Number(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {}
56
#if ARDUINOJSON_USE_DOUBLE
57
1.22k
  Number(double value) : type_(NumberType::Double), value_(value) {}
58
#endif
59
60
  template <typename T>
61
  T convertTo() const {
62
    switch (type_) {
63
      case NumberType::Float:
64
        return convertNumber<T>(value_.asFloat);
65
      case NumberType::SignedInteger:
66
        return convertNumber<T>(value_.asSignedInteger);
67
      case NumberType::UnsignedInteger:
68
        return convertNumber<T>(value_.asUnsignedInteger);
69
#if ARDUINOJSON_USE_DOUBLE
70
      case NumberType::Double:
71
        return convertNumber<T>(value_.asDouble);
72
#endif
73
      default:
74
        return T();
75
    }
76
  }
77
78
8.08k
  NumberType type() const {
79
8.08k
    return type_;
80
8.08k
  }
81
82
634
  JsonInteger asSignedInteger() const {
83
634
    ARDUINOJSON_ASSERT(type_ == NumberType::SignedInteger);
84
634
    return value_.asSignedInteger;
85
634
  }
86
87
2.11k
  JsonUInt asUnsignedInteger() const {
88
2.11k
    ARDUINOJSON_ASSERT(type_ == NumberType::UnsignedInteger);
89
2.11k
    return value_.asUnsignedInteger;
90
2.11k
  }
91
92
3.94k
  float asFloat() const {
93
3.94k
    ARDUINOJSON_ASSERT(type_ == NumberType::Float);
94
3.94k
    return value_.asFloat;
95
3.94k
  }
96
97
#if ARDUINOJSON_USE_DOUBLE
98
1.22k
  double asDouble() const {
99
1.22k
    ARDUINOJSON_ASSERT(type_ == NumberType::Double);
100
1.22k
    return value_.asDouble;
101
1.22k
  }
102
#endif
103
};
104
105
8.08k
inline Number parseNumber(const char* s) {
106
8.08k
  using traits = FloatTraits<JsonFloat>;
107
8.08k
  using mantissa_t = largest_type<traits::mantissa_type, JsonUInt>;
108
8.08k
  using exponent_t = traits::exponent_type;
109
110
8.08k
  ARDUINOJSON_ASSERT(s != 0);
111
112
8.08k
  bool is_negative = false;
113
8.08k
  switch (*s) {
114
942
    case '-':
115
942
      is_negative = true;
116
942
      s++;
117
942
      break;
118
75
    case '+':
119
75
      s++;
120
75
      break;
121
8.08k
  }
122
123
#if ARDUINOJSON_ENABLE_NAN
124
  if (*s == 'n' || *s == 'N') {
125
    return Number(traits::nan());
126
  }
127
#endif
128
129
#if ARDUINOJSON_ENABLE_INFINITY
130
  if (*s == 'i' || *s == 'I') {
131
    return Number(is_negative ? -traits::inf() : traits::inf());
132
  }
133
#endif
134
135
8.08k
  if (!isdigit(*s) && *s != '.')
136
120
    return Number();
137
138
7.96k
  mantissa_t mantissa = 0;
139
7.96k
  exponent_t exponent_offset = 0;
140
7.96k
  const mantissa_t maxUint = JsonUInt(-1);
141
142
25.7k
  while (isdigit(*s)) {
143
17.8k
    uint8_t digit = uint8_t(*s - '0');
144
17.8k
    if (mantissa > maxUint / 10)
145
65
      break;
146
17.7k
    mantissa *= 10;
147
17.7k
    if (mantissa > maxUint - digit)
148
18
      break;
149
17.7k
    mantissa += digit;
150
17.7k
    s++;
151
17.7k
  }
152
153
7.96k
  if (*s == '\0') {
154
2.76k
    if (is_negative) {
155
654
      const mantissa_t sintMantissaMax = mantissa_t(1)
156
654
                                         << (sizeof(JsonInteger) * 8 - 1);
157
654
      if (mantissa <= sintMantissaMax) {
158
634
        return Number(JsonInteger(~mantissa + 1));
159
634
      }
160
2.11k
    } else {
161
2.11k
      return Number(JsonUInt(mantissa));
162
2.11k
    }
163
2.76k
  }
164
165
  // avoid mantissa overflow
166
5.67k
  while (mantissa > traits::mantissa_max) {
167
454
    mantissa /= 10;
168
454
    exponent_offset++;
169
454
  }
170
171
  // remaing digits can't fit in the mantissa
172
5.61k
  while (isdigit(*s)) {
173
396
    exponent_offset++;
174
396
    s++;
175
396
  }
176
177
5.22k
  if (*s == '.') {
178
3.00k
    s++;
179
3.99k
    while (isdigit(*s)) {
180
994
      if (mantissa < traits::mantissa_max / 10) {
181
772
        mantissa = mantissa * 10 + uint8_t(*s - '0');
182
772
        exponent_offset--;
183
772
      }
184
994
      s++;
185
994
    }
186
3.00k
  }
187
188
5.22k
  int exponent = 0;
189
5.22k
  if (*s == 'e' || *s == 'E') {
190
2.12k
    s++;
191
2.12k
    bool negative_exponent = false;
192
2.12k
    if (*s == '-') {
193
385
      negative_exponent = true;
194
385
      s++;
195
1.73k
    } else if (*s == '+') {
196
66
      s++;
197
66
    }
198
199
5.27k
    while (isdigit(*s)) {
200
3.35k
      exponent = exponent * 10 + (*s - '0');
201
3.35k
      if (exponent + exponent_offset > traits::exponent_max) {
202
209
        if (negative_exponent)
203
68
          return Number(is_negative ? -0.0f : 0.0f);
204
141
        else
205
141
          return Number(is_negative ? -traits::inf() : traits::inf());
206
209
      }
207
3.14k
      s++;
208
3.14k
    }
209
1.91k
    if (negative_exponent)
210
317
      exponent = -exponent;
211
1.91k
  }
212
5.01k
  exponent += exponent_offset;
213
214
  // we should be at the end of the string, otherwise it's an error
215
5.01k
  if (*s != '\0')
216
49
    return Number();
217
218
4.96k
#if ARDUINOJSON_USE_DOUBLE
219
4.96k
  bool isDouble = exponent < -FloatTraits<float>::exponent_max ||
220
4.84k
                  exponent > FloatTraits<float>::exponent_max ||
221
4.18k
                  mantissa > FloatTraits<float>::mantissa_max;
222
4.96k
  if (isDouble) {
223
1.08k
    auto final_result = make_float(double(mantissa), exponent);
224
1.08k
    return Number(is_negative ? -final_result : final_result);
225
1.08k
  } else
226
3.87k
#endif
227
3.87k
  {
228
3.87k
    auto final_result = make_float(float(mantissa), exponent);
229
3.87k
    return Number(is_negative ? -final_result : final_result);
230
3.87k
  }
231
4.96k
}
232
233
template <typename T>
234
inline T parseNumber(const char* s) {
235
  return parseNumber(s).convertTo<T>();
236
}
237
238
ARDUINOJSON_END_PRIVATE_NAMESPACE