1 /**
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cstdlib>
17 #include "ets_intrinsics_helpers.h"
18 #include "include/mem/panda_string.h"
19 #include "types/ets_field.h"
20 #include "types/ets_string.h"
21
22 namespace panda::ets::intrinsics::helpers {
23
24 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
25 #define RETURN_IF_CONVERSION_END(p, end, result) \
26 if ((p) == (end)) { \
27 return (result); \
28 }
29
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)30 double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
31 {
32 if (IsEmptyString(start, end)) {
33 return NAN_VALUE;
34 }
35
36 radix = 0;
37 auto p = const_cast<uint8_t *>(start);
38
39 // 1. skip space and line terminal
40 if (!GotoNonspace(&p, end)) {
41 return 0.0;
42 }
43
44 // 2. get number sign
45 Sign sign = Sign::NONE;
46 if (*p == '+') {
47 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
48 sign = Sign::POS;
49 } else if (*p == '-') {
50 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
51 sign = Sign::NEG;
52 }
53 bool ignoreTrailing = (flags & flags::IGNORE_TRAILING) != 0;
54
55 // 3. judge Infinity
56 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
57 if (*p == INF[0]) {
58 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
59 for (const char *i = &INF[1]; *i != '\0'; ++i) {
60 if (++p == end || *p != *i) {
61 return NAN_VALUE;
62 }
63 }
64 ++p;
65 if (!ignoreTrailing && GotoNonspace(&p, end)) {
66 return NAN_VALUE;
67 }
68 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
69 }
70
71 // 4. get number radix
72 bool leadingZero = false;
73 bool prefixRadix = false;
74 if (*p == '0' && radix == 0) {
75 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
76 if (*p == 'x' || *p == 'X') {
77 if ((flags & flags::ALLOW_HEX) == 0) {
78 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
79 }
80 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
81 if (sign != Sign::NONE) {
82 return NAN_VALUE;
83 }
84 prefixRadix = true;
85 radix = HEXADECIMAL;
86 } else if (*p == 'o' || *p == 'O') {
87 if ((flags & flags::ALLOW_OCTAL) == 0) {
88 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
89 }
90 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
91 if (sign != Sign::NONE) {
92 return NAN_VALUE;
93 }
94 prefixRadix = true;
95 radix = OCTAL;
96 } else if (*p == 'b' || *p == 'B') {
97 if ((flags & flags::ALLOW_BINARY) == 0) {
98 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
99 }
100 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
101 if (sign != Sign::NONE) {
102 return NAN_VALUE;
103 }
104 prefixRadix = true;
105 radix = BINARY;
106 } else {
107 leadingZero = true;
108 }
109 }
110
111 if (radix == 0) {
112 radix = DECIMAL;
113 }
114 auto pStart = p;
115 // 5. skip leading '0'
116 while (*p == '0') {
117 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
118 leadingZero = true;
119 }
120 // 6. parse to number
121 uint64_t intNumber = 0;
122 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
123 int digits = 0;
124 int exponent = 0;
125 do {
126 uint8_t c = ToDigit(*p);
127 if (c >= radix) {
128 if (!prefixRadix || ignoreTrailing || (pStart != p && !GotoNonspace(&p, end))) {
129 break;
130 }
131 // "0b" "0x1.2" "0b1e2" ...
132 return NAN_VALUE;
133 }
134 ++digits;
135 if (intNumber < numberMax) {
136 intNumber = intNumber * radix + c;
137 } else {
138 ++exponent;
139 }
140 } while (++p != end);
141
142 auto number = static_cast<double>(intNumber);
143 if (sign == Sign::NEG) {
144 if (number == 0) {
145 number = -0.0;
146 } else {
147 number = -number;
148 }
149 }
150
151 // 7. deal with other radix except DECIMAL
152 if (p == end || radix != DECIMAL) {
153 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && GotoNonspace(&p, end))) {
154 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
155 return NAN_VALUE;
156 }
157 return number * std::pow(radix, exponent);
158 }
159
160 // 8. parse '.'
161 if (radix == DECIMAL && *p == '.') {
162 RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE);
163 while (ToDigit(*p) < radix) {
164 --exponent;
165 ++digits;
166 if (++p == end) {
167 break;
168 }
169 }
170 }
171 if (digits == 0 && !leadingZero) {
172 // no digits there, like ".", "sss", or ".e1"
173 return NAN_VALUE;
174 }
175 auto pEnd = p;
176
177 // 9. parse 'e/E' with '+/-'
178 char exponentSign = '+';
179 int additionalExponent = 0;
180 constexpr int MAX_EXPONENT = INT32_MAX / 2;
181 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
182 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
183
184 // 10. parse exponent number
185 if (*p == '+' || *p == '-') {
186 exponentSign = static_cast<char>(*p);
187 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
188 }
189 uint8_t digit;
190 while ((digit = ToDigit(*p)) < radix) {
191 if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
192 additionalExponent = MAX_EXPONENT;
193 } else {
194 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
195 }
196 if (++p == end) {
197 break;
198 }
199 }
200 }
201 exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
202 if (!ignoreTrailing && GotoNonspace(&p, end)) {
203 return NAN_VALUE;
204 }
205
206 // 10. build StringNumericLiteral string
207 PandaString buffer;
208 if (sign == Sign::NEG) {
209 buffer += "-";
210 }
211 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
212 if (*i != static_cast<uint8_t>('.')) {
213 buffer += *i;
214 }
215 }
216
217 // 11. convert none-prefix radix string
218 return Strtod(buffer.c_str(), exponent, radix);
219 }
220
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)221 double StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
222 {
223 auto p = const_cast<uint8_t *>(start);
224 // 1. skip space and line terminal
225 if (!GotoNonspace(&p, end)) {
226 return NAN_VALUE;
227 }
228
229 // 2. sign bit
230 bool negative = false;
231 if (*p == '-') {
232 negative = true;
233 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
234 } else if (*p == '+') {
235 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
236 }
237 // 3. 0x or 0X
238 bool stripPrefix = true;
239 // 4. If R != 0, then
240 // a. If R < 2 or R > 36, return NaN.
241 // b. If R != 16, let stripPrefix be false.
242 if (radix != 0) {
243 if (radix < MIN_RADIX || radix > MAX_RADIX) {
244 return NAN_VALUE;
245 }
246 if (radix != HEXADECIMAL) {
247 stripPrefix = false;
248 }
249 } else {
250 radix = DECIMAL;
251 }
252 int size = 0;
253 if (stripPrefix) {
254 if (*p == '0') {
255 size++;
256 if (++p != end && (*p == 'x' || *p == 'X')) {
257 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
258 radix = HEXADECIMAL;
259 }
260 }
261 }
262
263 double result = 0;
264 bool isDone = false;
265 do {
266 double part = 0;
267 uint32_t multiplier = 1;
268 for (; p != end; ++p) {
269 // The maximum value to ensure that uint32_t will not overflow
270 const uint32_t maxMultiper = 0xffffffffU / 36U;
271 uint32_t m = multiplier * static_cast<uint32_t>(radix);
272 if (m > maxMultiper) {
273 break;
274 }
275
276 int currentBit = static_cast<int>(ToDigit(*p));
277 if (currentBit >= radix) {
278 isDone = true;
279 break;
280 }
281 size++;
282 part = part * radix + currentBit;
283 multiplier = m;
284 }
285 result = result * multiplier + part;
286 if (isDone) {
287 break;
288 }
289 } while (p != end);
290
291 if (size == 0) {
292 return NAN_VALUE;
293 }
294
295 return negative ? -result : result;
296 }
297
DoubleToExponential(double number,int digit)298 EtsString *DoubleToExponential(double number, int digit)
299 {
300 PandaStringStream ss;
301 if (digit < 0) {
302 ss << std::setiosflags(std::ios::scientific) << std::setprecision(MAX_PRECISION) << number;
303 } else {
304 ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
305 }
306 PandaString result = ss.str();
307 size_t found = result.find_last_of('e');
308 if (found != PandaString::npos && found < result.size() - 2U && result[found + 2U] == '0') {
309 result.erase(found + 2U, 1); // 2:offset of e
310 }
311 if (digit < 0) {
312 size_t end = found;
313 while (--found > 0) {
314 if (result[found] != '0') {
315 break;
316 }
317 }
318 if (result[found] == '.') {
319 found--;
320 }
321 if (found < end - 1) {
322 result.erase(found + 1, end - found - 1);
323 }
324 }
325 return EtsString::CreateFromMUtf8(result.c_str());
326 }
327
DoubleToFixed(double number,int digit)328 EtsString *DoubleToFixed(double number, int digit)
329 {
330 PandaStringStream ss;
331 ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
332 return EtsString::CreateFromMUtf8(ss.str().c_str());
333 }
334
DoubleToPrecision(double number,int digit)335 EtsString *DoubleToPrecision(double number, int digit)
336 {
337 if (number == 0.0) {
338 return DoubleToFixed(number, digit - 1);
339 }
340 PandaStringStream ss;
341 double positiveNumber = number > 0 ? number : -number;
342 int logDigit = std::floor(log10(positiveNumber));
343 int radixDigit = digit - logDigit - 1;
344 const int maxExponentDigit = 6;
345 if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= maxExponentDigit)) {
346 return DoubleToFixed(number, std::abs(radixDigit));
347 }
348 return DoubleToExponential(number, digit - 1);
349 }
350
GetStdDoubleArgument(ObjectHeader * obj)351 double GetStdDoubleArgument(ObjectHeader *obj)
352 {
353 auto *cls = obj->ClassAddr<Class>();
354
355 // Assume std.core.Double has only one `double` field
356 ASSERT(cls->GetInstanceFields().size() == 1);
357
358 Field &fieldVal = cls->GetInstanceFields()[0];
359
360 ASSERT(fieldVal.GetTypeId() == panda_file::Type::TypeId::F64);
361
362 size_t offset = fieldVal.GetOffset();
363 return obj->GetFieldPrimitive<double>(offset);
364 }
365
366 } // namespace panda::ets::intrinsics::helpers
367