/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 |