1 /**
2 * Copyright (c) 2021-2024 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 "dtoa_helper.h"
18 #include "ets_intrinsics_helpers.h"
19 #include "include/mem/panda_string.h"
20 #include "types/ets_field.h"
21 #include "types/ets_string.h"
22
23 namespace ark::ets::intrinsics::helpers {
24
25 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
26 #define RETURN_IF_CONVERSION_END(p, end, result) \
27 if ((p) == (end)) { \
28 return (result); \
29 }
30
31 namespace parse_helpers {
32
33 template <typename ResultType>
34 struct ParseResult {
35 ResultType value;
36 uint8_t *pointerPosition = nullptr;
37 bool isSuccess = false;
38 };
39
ParseExponent(const uint8_t * start,const uint8_t * end,const uint8_t radix,const uint32_t flags)40 ParseResult<int32_t> ParseExponent(const uint8_t *start, const uint8_t *end, const uint8_t radix, const uint32_t flags)
41 {
42 constexpr int32_t MAX_EXPONENT = INT32_MAX / 2;
43 auto p = const_cast<uint8_t *>(start);
44 if (radix == 0) {
45 return {0, p, false};
46 }
47
48 char exponentSign = '+';
49 int32_t additionalExponent = 0;
50 bool undefinedExponent = false;
51 ++p;
52 if (p == end) {
53 undefinedExponent = true;
54 }
55
56 if (!undefinedExponent && (*p == '+' || *p == '-')) {
57 exponentSign = static_cast<char>(*p);
58 ++p;
59 if (p == end) {
60 undefinedExponent = true;
61 }
62 }
63 if (!undefinedExponent) {
64 uint8_t digit;
65 while ((digit = ToDigit(*p)) < radix) {
66 if (additionalExponent > MAX_EXPONENT / radix) {
67 additionalExponent = MAX_EXPONENT;
68 } else {
69 additionalExponent = additionalExponent * static_cast<int32_t>(radix) + static_cast<int32_t>(digit);
70 }
71 if (++p == end) {
72 break;
73 }
74 }
75 } else if ((flags & flags::ERROR_IN_EXPONENT_IS_NAN) != 0) {
76 return {0, p, false};
77 }
78 if (exponentSign == '-') {
79 return {-additionalExponent, p, true};
80 }
81 return {additionalExponent, p, true};
82 }
83
84 } // namespace parse_helpers
85
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)86 double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
87 {
88 // 1. skip space and line terminal
89 if (IsEmptyString(start, end)) {
90 if ((flags & flags::EMPTY_IS_ZERO) != 0) {
91 return 0.0;
92 }
93 return NAN_VALUE;
94 }
95
96 radix = 0;
97 auto p = const_cast<uint8_t *>(start);
98
99 GotoNonspace(&p, end);
100
101 // 2. get number sign
102 Sign sign = Sign::NONE;
103 if (*p == '+') {
104 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
105 sign = Sign::POS;
106 } else if (*p == '-') {
107 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
108 sign = Sign::NEG;
109 }
110 bool ignoreTrailing = (flags & flags::IGNORE_TRAILING) != 0;
111
112 // 3. judge Infinity
113 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
114 if (*p == INF[0]) {
115 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
116 for (const char *i = &INF[1]; *i != '\0'; ++i) {
117 if (++p == end || *p != *i) {
118 return NAN_VALUE;
119 }
120 }
121 ++p;
122 if (!ignoreTrailing && GotoNonspace(&p, end)) {
123 return NAN_VALUE;
124 }
125 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
126 }
127
128 // 4. get number radix
129 bool leadingZero = false;
130 bool prefixRadix = false;
131 if (*p == '0' && radix == 0) {
132 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
133 if (*p == 'x' || *p == 'X') {
134 if ((flags & flags::ALLOW_HEX) == 0) {
135 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
136 }
137 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
138 if (sign != Sign::NONE) {
139 return NAN_VALUE;
140 }
141 prefixRadix = true;
142 radix = HEXADECIMAL;
143 } else if (*p == 'o' || *p == 'O') {
144 if ((flags & flags::ALLOW_OCTAL) == 0) {
145 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
146 }
147 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
148 if (sign != Sign::NONE) {
149 return NAN_VALUE;
150 }
151 prefixRadix = true;
152 radix = OCTAL;
153 } else if (*p == 'b' || *p == 'B') {
154 if ((flags & flags::ALLOW_BINARY) == 0) {
155 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
156 }
157 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
158 if (sign != Sign::NONE) {
159 return NAN_VALUE;
160 }
161 prefixRadix = true;
162 radix = BINARY;
163 } else {
164 leadingZero = true;
165 }
166 }
167
168 if (radix == 0) {
169 radix = DECIMAL;
170 }
171 auto pStart = p;
172 // 5. skip leading '0'
173 while (*p == '0') {
174 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
175 leadingZero = true;
176 }
177 // 6. parse to number
178 uint64_t intNumber = 0;
179 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
180 int digits = 0;
181 int exponent = 0;
182 do {
183 uint8_t c = ToDigit(*p);
184 if (c >= radix) {
185 if (!prefixRadix || ignoreTrailing || (pStart != p && !GotoNonspace(&p, end))) {
186 break;
187 }
188 // "0b" "0x1.2" "0b1e2" ...
189 return NAN_VALUE;
190 }
191 ++digits;
192 if (intNumber < numberMax) {
193 intNumber = intNumber * radix + c;
194 } else {
195 ++exponent;
196 }
197 } while (++p != end);
198
199 auto number = static_cast<double>(intNumber);
200 if (sign == Sign::NEG) {
201 if (number == 0) {
202 number = -0.0;
203 } else {
204 number = -number;
205 }
206 }
207
208 // 7. deal with other radix except DECIMAL
209 if (p == end || radix != DECIMAL) {
210 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && GotoNonspace(&p, end))) {
211 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
212 return NAN_VALUE;
213 }
214 return number * std::pow(radix, exponent);
215 }
216
217 // 8. parse '.'
218 if (*p == '.') {
219 RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE);
220 while (ToDigit(*p) < radix) {
221 --exponent;
222 ++digits;
223 if (++p == end) {
224 break;
225 }
226 }
227 }
228 if (digits == 0 && !leadingZero) {
229 // no digits there, like ".", "sss", or ".e1"
230 return NAN_VALUE;
231 }
232 auto pEnd = p;
233
234 // 9. parse 'e/E' with '+/-'
235 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
236 auto parseExponentResult = parse_helpers::ParseExponent(p, end, radix, flags);
237 if (!parseExponentResult.isSuccess) {
238 return NAN_VALUE;
239 }
240 p = parseExponentResult.pointerPosition;
241 exponent += parseExponentResult.value;
242 }
243 if (!ignoreTrailing && GotoNonspace(&p, end)) {
244 return NAN_VALUE;
245 }
246
247 // 10. build StringNumericLiteral string
248 PandaString buffer;
249 if (sign == Sign::NEG) {
250 buffer += "-";
251 }
252 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
253 if (*i != static_cast<uint8_t>('.')) {
254 buffer += *i;
255 }
256 }
257
258 // 11. convert none-prefix radix string
259 return Strtod(buffer.c_str(), exponent, radix);
260 }
261
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)262 double StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
263 {
264 auto p = const_cast<uint8_t *>(start);
265 // 1. skip space and line terminal
266 if (!GotoNonspace(&p, end)) {
267 return NAN_VALUE;
268 }
269
270 // 2. sign bit
271 bool negative = false;
272 if (*p == '-') {
273 negative = true;
274 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
275 } else if (*p == '+') {
276 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
277 }
278 // 3. 0x or 0X
279 bool stripPrefix = true;
280 // 4. If R != 0, then
281 // a. If R < 2 or R > 36, return NaN.
282 // b. If R != 16, let stripPrefix be false.
283 if (radix != 0) {
284 if (radix < MIN_RADIX || radix > MAX_RADIX) {
285 return NAN_VALUE;
286 }
287 if (radix != HEXADECIMAL) {
288 stripPrefix = false;
289 }
290 } else {
291 radix = DECIMAL;
292 }
293 int size = 0;
294 if (stripPrefix) {
295 if (*p == '0') {
296 size++;
297 if (++p != end && (*p == 'x' || *p == 'X')) {
298 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
299 radix = HEXADECIMAL;
300 }
301 }
302 }
303
304 double result = 0;
305 bool isDone = false;
306 do {
307 double part = 0;
308 uint32_t multiplier = 1;
309 for (; p != end; ++p) {
310 // The maximum value to ensure that uint32_t will not overflow
311 const uint32_t maxMultiper = 0xffffffffU / 36U;
312 uint32_t m = multiplier * static_cast<uint32_t>(radix);
313 if (m > maxMultiper) {
314 break;
315 }
316
317 int currentBit = static_cast<int>(ToDigit(*p));
318 if (currentBit >= radix) {
319 isDone = true;
320 break;
321 }
322 size++;
323 part = part * radix + currentBit;
324 multiplier = m;
325 }
326 result = result * multiplier + part;
327 if (isDone) {
328 break;
329 }
330 } while (p != end);
331
332 if (size == 0) {
333 return NAN_VALUE;
334 }
335
336 return negative ? -result : result;
337 }
338
DoubleToExponential(double number,int digit)339 EtsString *DoubleToExponential(double number, int digit)
340 {
341 PandaStringStream ss;
342 if (digit < 0) {
343 ss << std::setiosflags(std::ios::scientific) << std::setprecision(MAX_PRECISION) << number;
344 } else {
345 ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
346 }
347 PandaString result = ss.str();
348 size_t found = result.find_last_of('e');
349 if (found != PandaString::npos && found < result.size() - 2U && result[found + 2U] == '0') {
350 result.erase(found + 2U, 1); // 2:offset of e
351 }
352 if (digit < 0) {
353 size_t end = found;
354 while (--found > 0) {
355 if (result[found] != '0') {
356 break;
357 }
358 }
359 if (result[found] == '.') {
360 found--;
361 }
362 if (found < end - 1) {
363 result.erase(found + 1, end - found - 1);
364 }
365 }
366 return EtsString::CreateFromMUtf8(result.c_str());
367 }
368
DoubleToFixed(double number,int digit)369 EtsString *DoubleToFixed(double number, int digit)
370 {
371 PandaStringStream ss;
372 ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
373 return EtsString::CreateFromMUtf8(ss.str().c_str());
374 }
375
DoubleToPrecision(double number,int digit)376 EtsString *DoubleToPrecision(double number, int digit)
377 {
378 if (number == 0.0) {
379 return DoubleToFixed(number, digit - 1);
380 }
381 PandaStringStream ss;
382 double positiveNumber = number > 0 ? number : -number;
383 int logDigit = std::floor(log10(positiveNumber));
384 int radixDigit = digit - logDigit - 1;
385 const int maxExponentDigit = 6;
386 if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= maxExponentDigit)) {
387 return DoubleToFixed(number, std::abs(radixDigit));
388 }
389 return DoubleToExponential(number, digit - 1);
390 }
391
GetStdDoubleArgument(ObjectHeader * obj)392 double GetStdDoubleArgument(ObjectHeader *obj)
393 {
394 auto *cls = obj->ClassAddr<Class>();
395
396 // Assume std.core.Double has only one `double` field
397 ASSERT(cls->GetInstanceFields().size() == 1);
398
399 Field &fieldVal = cls->GetInstanceFields()[0];
400
401 ASSERT(fieldVal.GetTypeId() == panda_file::Type::TypeId::F64);
402
403 size_t offset = fieldVal.GetOffset();
404 return obj->GetFieldPrimitive<double>(offset);
405 }
406
407 // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
408 template <typename FpType, std::enable_if_t<std::is_floating_point_v<FpType>, bool> = true>
GetBase(FpType d,int digits,int * decpt,Span<char> buf)409 void GetBase(FpType d, int digits, int *decpt, Span<char> buf)
410 {
411 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
412 auto ret = snprintf_s(buf.begin(), buf.size(), buf.size() - 1, "%.*e", digits - 1, d);
413 if (ret == -1) {
414 LOG(FATAL, ETS) << "snprintf_s failed";
415 UNREACHABLE();
416 }
417 char *end = buf.begin() + ret;
418 ASSERT(*end == 0);
419 const size_t positive = (digits > 1) ? 1 : 0;
420 char *ePos = buf.begin() + digits + positive;
421 ASSERT(*ePos == 'e');
422 char *signPos = ePos + 1;
423 char *from = signPos + 1;
424 // exponent
425 if (std::from_chars(from, end, *decpt).ec != std::errc()) {
426 UNREACHABLE();
427 }
428 if (*signPos == '-') {
429 *decpt *= -1;
430 }
431 ++*decpt;
432 }
433
434 constexpr bool USE_GET_BASE_FAST =
435 #ifdef __cpp_lib_to_chars
436 true;
437 #else
438 false;
439 #endif
440
441 template <typename FpType, std::enable_if_t<std::is_floating_point_v<FpType>, bool> = true>
GetBaseFast(FpType d,int * decpt,Span<char> buf)442 int GetBaseFast([[maybe_unused]] FpType d, int *decpt, Span<char> buf)
443 {
444 ASSERT(d >= 0);
445 char *end;
446 #ifdef __cpp_lib_to_chars
447 auto ret = std::to_chars(buf.begin(), buf.end(), d, std::chars_format::scientific);
448 if (ret.ec != std::errc()) {
449 LOG(FATAL, ETS) << "to_chars failed";
450 UNREACHABLE();
451 }
452 end = ret.ptr;
453 #else
454 UNREACHABLE();
455 #endif
456 *end = '\0';
457 // exponent
458 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
459 auto from = std::find(buf.begin(), end, 'e');
460 auto digits = from - buf.begin();
461 if (digits > 1) {
462 digits--;
463 }
464 ASSERT(from != end);
465 if (std::from_chars(from + 2U, end, *decpt).ec != std::errc()) {
466 UNREACHABLE();
467 }
468 if (from[1] == '-') {
469 *decpt *= -1;
470 }
471 ++*decpt;
472 return digits;
473 }
474
475 template <typename FpType>
GetBaseBinarySearch(FpType d,int * decpt,Span<char> buf)476 int GetBaseBinarySearch(FpType d, int *decpt, Span<char> buf)
477 {
478 // find the minimum amount of digits
479 int minDigits = 1;
480 int maxDigits = std::is_same_v<FpType, double> ? DOUBLE_MAX_PRECISION : FLOAT_MAX_PRECISION;
481 int digits;
482
483 while (minDigits < maxDigits) {
484 digits = (minDigits + maxDigits) / 2_I;
485 GetBase(d, digits, decpt, buf);
486
487 bool same = StrToFp<FpType>(buf.begin(), nullptr) == d;
488 if (same) {
489 // no need to keep the trailing zeros
490 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
491 while (digits >= 2_I && buf[digits] == '0') { // 2 means ignore the integer and point
492 digits--;
493 }
494 maxDigits = digits;
495 } else {
496 minDigits = digits + 1;
497 }
498 }
499 digits = maxDigits;
500 GetBase(d, digits, decpt, buf);
501 return digits;
502 }
503
504 template <typename FpType>
RecheckGetMinimumDigits(FpType d,Span<char> buf)505 [[maybe_unused]] static bool RecheckGetMinimumDigits(FpType d, Span<char> buf)
506 {
507 ASSERT(StrToFp<FpType>(buf.begin(), nullptr) == d);
508 std::string str(buf.begin());
509 auto pos = str.find('e');
510 std::copy(str.begin() + pos, str.end(), str.begin() + pos - 1);
511 str[str.size() - 1] = '\0';
512 return StrToFp<FpType>(str.data(), nullptr) != d;
513 }
514
515 // result is written starting with buf[1]
516 template <typename FpType>
GetMinimumDigits(FpType d,int * decpt,Span<char> buf)517 int GetMinimumDigits(FpType d, int *decpt, Span<char> buf)
518 {
519 if (std::is_same_v<FpType, double>) {
520 DtoaHelper helper(buf.begin() + 1);
521 helper.Dtoa(d);
522 *decpt = helper.GetPoint();
523 return helper.GetDigits();
524 }
525 int digits;
526 if constexpr (USE_GET_BASE_FAST) {
527 digits = GetBaseFast(d, decpt, buf);
528 } else {
529 digits = GetBaseBinarySearch(d, decpt, buf);
530 }
531 ASSERT(RecheckGetMinimumDigits(d, buf));
532 ASSERT(digits == 1 || buf[1] == '.');
533 buf[1] = buf[0];
534 return digits;
535 }
536
537 template <typename FpType>
SmallFpToString(FpType number,bool negative,char * buffer)538 char *SmallFpToString(FpType number, bool negative, char *buffer)
539 {
540 using SignedInt = typename ark::helpers::TypeHelperT<sizeof(FpType) * CHAR_BIT, true>;
541 if (negative) {
542 *(buffer++) = '-';
543 }
544 *(buffer++) = '0';
545 *(buffer++) = '.';
546 SignedInt power = TEN;
547 SignedInt s = 0;
548 int maxDigits = std::is_same_v<FpType, double> ? DOUBLE_MAX_PRECISION : FLOAT_MAX_PRECISION;
549 int digits = maxDigits;
550 for (int k = 1; k <= maxDigits; ++k) {
551 s = static_cast<SignedInt>(number * power);
552 if (k == maxDigits || s / static_cast<FpType>(power) == number) { // s * (10 ** -k)
553 digits = k;
554 break;
555 }
556 power *= TEN;
557 }
558 for (int k = digits - 1; k >= 0; k--) {
559 auto digit = s % TEN;
560 s /= TEN;
561 *(buffer + k) = '0' + digit;
562 }
563 return buffer + digits;
564 }
565
566 template <typename FpType>
FpToStringDecimalRadixMainCase(FpType number,bool negative,Span<char> buffer)567 Span<char> FpToStringDecimalRadixMainCase(FpType number, bool negative, Span<char> buffer)
568 {
569 auto bufferStart = buffer.begin() + 2U;
570 ASSERT(number > 0);
571 int n = 0;
572 int k = intrinsics::helpers::GetMinimumDigits(number, &n, buffer.SubSpan(1));
573 auto bufferEnd = bufferStart + k;
574
575 if (0 < n && n <= 21_I) { // NOLINT(readability-magic-numbers)
576 if (k <= n) {
577 // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
578 // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
579 // 0x0030 (DIGIT ZERO).
580 std::fill_n(bufferEnd, n - k, '0');
581 bufferEnd += n - k;
582 } else {
583 // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
584 // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
585 // the remaining k−n digits of the decimal representation of s.
586 auto fracStart = bufferStart + n;
587 if (memmove_s(fracStart + 1, buffer.end() - (fracStart + 1), fracStart, k - n) != EOK) {
588 UNREACHABLE();
589 }
590 *fracStart = '.';
591 bufferEnd++;
592 }
593 } else if (-6_I < n && n <= 0) { // NOLINT(readability-magic-numbers)
594 // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
595 // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
596 // code units of the k digits of the decimal representation of s.
597 auto length = -n + 2U;
598 auto fracStart = bufferStart + length;
599 if (memmove_s(fracStart, buffer.end() - fracStart, bufferStart, k) != EOK) {
600 UNREACHABLE();
601 }
602 std::fill_n(bufferStart, length, '0');
603 bufferStart[1] = '.';
604 bufferEnd += length;
605 } else {
606 if (k == 1) {
607 // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
608 ASSERT(bufferEnd == bufferStart + 1);
609 } else {
610 *(bufferStart - 1) = *bufferStart;
611 *(bufferStart--) = '.';
612 }
613 // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
614 // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
615 // the decimal representation of the integer abs(n−1) (with no leading zeroes).
616 *(bufferEnd++) = 'e';
617 if (n >= 1) {
618 *(bufferEnd++) = '+';
619 }
620 bufferEnd = std::to_chars(bufferEnd, buffer.end(), n - 1).ptr;
621 }
622 if (negative) {
623 *--bufferStart = '-';
624 }
625 return {bufferStart, bufferEnd};
626 }
627 // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
628
629 template char *SmallFpToString<double>(double number, bool negative, char *buffer);
630 template char *SmallFpToString<float>(float number, bool negative, char *buffer);
631
632 template Span<char> FpToStringDecimalRadixMainCase<double>(double number, bool negative, Span<char> buffer);
633 template Span<char> FpToStringDecimalRadixMainCase<float>(float number, bool negative, Span<char> buffer);
634
635 } // namespace ark::ets::intrinsics::helpers
636