• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "ecmascript/base/number_helper.h"
17 
18 #include <cmath>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iomanip>
22 #include <sstream>
23 #include <sys/time.h>
24 
25 #include "ecmascript/base/builtins_base.h"
26 #include "ecmascript/base/string_helper.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/object_factory.h"
29 
30 namespace panda::ecmascript::base {
31 enum class Sign { NONE, NEG, POS };
32 thread_local uint64_t RandomGenerator::randomState_ {0};
33 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
34 #define RETURN_IF_CONVERSION_END(p, end, result) \
35     if ((p) == (end)) {                          \
36         return (result);                         \
37     }
38 
39 constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz";  // NOLINT (modernize-avoid-c-arrays)
40 constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
41 
ToDigit(uint8_t c)42 static inline uint8_t ToDigit(uint8_t c)
43 {
44     if (c >= '0' && c <= '9') {
45         return c - '0';
46     }
47     if (c >= 'A' && c <= 'Z') {
48         return c - 'A' + DECIMAL;
49     }
50     if (c >= 'a' && c <= 'z') {
51         return c - 'a' + DECIMAL;
52     }
53     return '$';
54 }
55 
GotoNonspace(uint8_t ** ptr,const uint8_t * end)56 bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
57 {
58     while (*ptr < end) {
59         uint16_t c = **ptr;
60         size_t size = 1;
61         if (c > INT8_MAX) {
62             size = 0;
63             uint16_t utf8Bit = INT8_MAX + 1;  // equal 0b1000'0000
64             while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
65                 ++size;
66                 utf8Bit >>= 1UL;
67             }
68             if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1, 0) <= 0) {
69                 return true;
70             }
71         }
72         if (!StringHelper::IsNonspace(c)) {
73             return true;
74         }
75         *ptr += size;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
76     }
77     return false;
78 }
79 
SignedZero(Sign sign)80 static inline double SignedZero(Sign sign)
81 {
82     return sign == Sign::NEG ? -0.0 : 0.0;
83 }
84 
IsEmptyString(const uint8_t * start,const uint8_t * end)85 bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
86 {
87     auto p = const_cast<uint8_t *>(start);
88     return !NumberHelper::GotoNonspace(&p, end);
89 }
90 
DoubleToString(JSThread * thread,double number,int radix)91 JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
92 {
93     bool negative = false;
94     if (number < 0.0) {
95         negative = true;
96         number = -number;
97     }
98 
99     double numberInteger = std::floor(number);
100     double numberFraction = number - numberInteger;
101 
102     auto value = bit_cast<uint64_t>(number);
103     value += 1;
104     double delta = HALF * (bit_cast<double>(value) - number);
105     delta = std::max(delta, bit_cast<double>(static_cast<uint64_t>(1))); // 1 : The binary of the smallest double is 1
106     CString result;
107     if (numberFraction != 0 && numberFraction >= delta) {
108         result += ".";
109         result += DecimalsToString(&numberInteger, numberFraction, radix, delta);
110     }
111 
112     result = IntegerToString(numberInteger, radix) + result;
113 
114     if (negative) {
115         result = "-" + result;
116     }
117 
118     return BuiltinsBase::GetTaggedString(thread, result.c_str());
119 }
120 
DoubleToExponential(JSThread * thread,double number,int digit)121 JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
122 {
123     CStringStream ss;
124     if (digit < 0) {
125         ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number;
126     } else {
127         ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
128     }
129     CString result = ss.str();
130     size_t found = result.find_last_of('e');
131     if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') {
132         result.erase(found + 2, 1); // 2:offset of e
133     }
134     if (digit < 0) {
135         size_t end = found;
136         while (--found > 0) {
137             if (result[found] != '0') {
138                 break;
139             }
140         }
141         if (result[found] == '.') {
142             found--;
143         }
144         if (found < end - 1) {
145             result.erase(found + 1, end - found - 1);
146         }
147     }
148     return BuiltinsBase::GetTaggedString(thread, result.c_str());
149 }
150 
DoubleToFixed(JSThread * thread,double number,int digit)151 JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit)
152 {
153     CStringStream ss;
154     ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
155     return BuiltinsBase::GetTaggedString(thread, ss.str().c_str());
156 }
157 
DoubleToPrecision(JSThread * thread,double number,int digit)158 JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit)
159 {
160     if (number == 0.0) {
161         return DoubleToFixed(thread, number, digit - 1);
162     }
163     CStringStream ss;
164     double positiveNumber = number > 0 ? number : -number;
165     int logDigit = std::floor(log10(positiveNumber));
166     int radixDigit = digit - logDigit - 1;
167     const int MAX_EXPONENT_DIGIT = 6;
168     if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) {
169         return DoubleToFixed(thread, number, std::abs(radixDigit));
170     }
171     return DoubleToExponential(thread, number, digit - 1);
172 }
173 
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)174 JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
175 {
176     auto p = const_cast<uint8_t *>(start);
177     JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
178     // 1. skip space and line terminal
179     if (!NumberHelper::GotoNonspace(&p, end)) {
180         return nanResult;
181     }
182 
183     // 2. sign bit
184     bool negative = false;
185     if (*p == '-') {
186         negative = true;
187         RETURN_IF_CONVERSION_END(++p, end, nanResult);
188     } else if (*p == '+') {
189         RETURN_IF_CONVERSION_END(++p, end, nanResult);
190     }
191     // 3. 0x or 0X
192     bool stripPrefix = true;
193     // 4. If R  0, then
194     //     a. If R < 2 or R > 36, return NaN.
195     //     b. If R  16, let stripPrefix be false.
196     if (radix != 0) {
197         if (radix < MIN_RADIX || radix > MAX_RADIX) {
198             return nanResult;
199         }
200         if (radix != HEXADECIMAL) {
201             stripPrefix = false;
202         }
203     } else {
204         radix = DECIMAL;
205     }
206     int size = 0;
207     if (stripPrefix) {
208         if (*p == '0') {
209             size++;
210             if (++p != end && (*p == 'x' || *p == 'X')) {
211                 RETURN_IF_CONVERSION_END(++p, end, nanResult);
212                 radix = HEXADECIMAL;
213             }
214         }
215     }
216 
217     double result = 0;
218     bool isDone = false;
219     do {
220         double part = 0;
221         uint32_t multiplier = 1;
222         for (; p != end; ++p) {
223             // The maximum value to ensure that uint32_t will not overflow
224             const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
225             uint32_t m = multiplier * static_cast<uint32_t>(radix);
226             if (m > MAX_MULTIPER) {
227                 break;
228             }
229 
230             int currentBit = static_cast<int>(ToDigit(*p));
231             if (currentBit >= radix) {
232                 isDone = true;
233                 break;
234             }
235             size++;
236             part = part * radix + currentBit;
237             multiplier = m;
238         }
239         result = result * multiplier + part;
240         if (isDone) {
241             break;
242         }
243     } while (p != end);
244 
245     if (size == 0) {
246         return nanResult;
247     }
248 
249     if (negative) {
250         result = -result;
251     }
252     return BuiltinsBase::GetTaggedDouble(result);
253 }
254 
Carry(char current,int radix)255 char NumberHelper::Carry(char current, int radix)
256 {
257     int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
258     digit = (digit == (radix - 1)) ? 0 : digit + 1;
259     return CHARS[digit];
260 }
261 
IntegerToString(double number,int radix)262 CString NumberHelper::IntegerToString(double number, int radix)
263 {
264     ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
265     CString result;
266     while (number / radix > MAX_MANTISSA) {
267         number /= radix;
268         result = CString("0").append(result);
269     }
270     do {
271         double remainder = std::fmod(number, radix);
272         result = CHARS[static_cast<int>(remainder)] + result;
273         number = (number - remainder) / radix;
274     } while (number > 0);
275     return result;
276 }
277 
DecimalsToString(double * numberInteger,double fraction,int radix,double delta)278 CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta)
279 {
280     CString result;
281     while (fraction >= delta) {
282         fraction *= radix;
283         delta *= radix;
284         int64_t integer = std::floor(fraction);
285         fraction -= integer;
286         result += CHARS[integer];
287         if (fraction > HALF && fraction + delta > 1) {
288             size_t fractionEnd = result.size() - 1;
289             result[fractionEnd] = Carry(*result.rbegin(), radix);
290             for (; fractionEnd > 0; fractionEnd--) {
291                 if (result[fractionEnd] == '0') {
292                     result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix);
293                 } else {
294                     break;
295                 }
296             }
297             if (fractionEnd == 0) {
298                 (*numberInteger)++;
299             }
300             break;
301         }
302     }
303     // delete 0 in the end
304     size_t found = result.find_last_not_of('0');
305     if (found != CString::npos) {
306         result.erase(found + 1);
307     }
308 
309     return result;
310 }
311 
IntToString(int number)312 CString NumberHelper::IntToString(int number)
313 {
314     return ToCString(number);
315 }
316 
IntToEcmaString(const JSThread * thread,int number)317 JSHandle<EcmaString> NumberHelper::IntToEcmaString(const JSThread *thread, int number)
318 {
319     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
320     return factory->NewFromASCII(ToCString(number));
321 }
322 
323 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)324 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
325 {
326     ASSERT(number.IsNumber());
327     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
328     if (number.IsInt()) {
329         return factory->NewFromASCII(IntToString(number.GetInt()));
330     }
331 
332     double d = number.GetDouble();
333     if (std::isnan(d)) {
334         return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNanCapitalString());
335     }
336     if (d == 0.0) {
337         return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
338     }
339     if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
340         return factory->NewFromASCII(IntToString(static_cast<int32_t>(d)));
341     }
342 
343     std::string result;
344     if (d < 0) {
345         result += "-";
346         d = -d;
347     }
348 
349     if (std::isinf(d)) {
350         result += "Infinity";
351         return factory->NewFromASCII(result.c_str());
352     }
353 
354     ASSERT(d > 0);
355 
356     // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m,
357     // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
358     // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note
359     // that k is the number of digits in the decimal representation of s and that s is not divisible by 10.
360     if (0.1 <= d && d < 1) {  // 0.1: 10 ** -1
361         // Fast path. In this case, n==0, just need to calculate k and s.
362         std::string resultFast = "0.";
363         int64_t sFast = 0;
364         int kFast = 1;
365         int64_t power = 1;
366         while (kFast <= DOUBLE_MAX_PRECISION) {
367             power *= 10;  // 10: base 10
368             int digitFast = static_cast<int64_t>(d * power) % 10;  // 10: base 10
369             ASSERT(0 <= digitFast && digitFast <= 9);  // 9: single digit max
370             sFast = sFast * 10 + digitFast;  // 10: base 10
371             resultFast += (digitFast + '0');
372             if (sFast / static_cast<double>(power) == d) {  // s * (10 ** -k)
373                 result += resultFast;
374                 return factory->NewFromASCII(result.c_str());
375             }
376             kFast++;
377         }
378     }
379     char buffer[JS_DTOA_BUF_SIZE] = {0};
380     int n = 0;
381     int k = GetMinmumDigits(d, &n, buffer);
382     std::string base = buffer;
383     if (n > 0 && n <= 21) {  // NOLINT(readability-magic-numbers)
384         base.erase(1, 1);
385         if (k <= n) {
386             // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
387             // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
388             // 0x0030 (DIGIT ZERO).
389             base += std::string(n - k, '0');
390         } else {
391             // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
392             // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
393             // the remaining k−n digits of the decimal representation of s.
394             base.insert(n, 1, '.');
395         }
396     } else if (-6 < n && n <= 0) {  // NOLINT(readability-magic-numbers)
397         // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
398         // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
399         // code units of the k digits of the decimal representation of s.
400         base.erase(1, 1);
401         base = std::string("0.") + std::string(-n, '0') + base;
402     } else {
403         if (k == 1) {
404             // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
405             base.erase(1, 1);
406         }
407         // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
408         // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
409         // the decimal representation of the integer abs(n−1) (with no leading zeroes).
410         base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
411     }
412     result += base;
413     return factory->NewFromASCII(result.c_str());
414 }
415 
TruncateDouble(double d)416 double NumberHelper::TruncateDouble(double d)
417 {
418     if (std::isnan(d)) {
419         return 0;
420     }
421     if (!std::isfinite(d)) {
422         return d;
423     }
424     // -0 to +0
425     if (d == 0.0) {
426         return 0;
427     }
428     return (d >= 0) ? std::floor(d) : std::ceil(d);
429 }
430 
DoubleToInt64(double d)431 int64_t NumberHelper::DoubleToInt64(double d)
432 {
433     if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
434         return std::numeric_limits<int64_t>::max();
435     }
436     if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
437         return std::numeric_limits<int64_t>::min();
438     }
439     return static_cast<int64_t>(d);
440 }
441 
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)442 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
443 {
444     auto p = const_cast<uint8_t *>(start);
445     // 1. skip space and line terminal
446     if (!NumberHelper::GotoNonspace(&p, end)) {
447         return 0.0;
448     }
449 
450     // 2. get number sign
451     Sign sign = Sign::NONE;
452     if (*p == '+') {
453         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
454         sign = Sign::POS;
455     } else if (*p == '-') {
456         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
457         sign = Sign::NEG;
458     }
459     bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
460 
461     // 3. judge Infinity
462     static const char INF[] = "Infinity";  // NOLINT(modernize-avoid-c-arrays)
463     if (*p == INF[0]) {
464         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
465         for (const char *i = &INF[1]; *i != '\0'; ++i) {
466             if (++p == end || *p != *i) {
467                 return NAN_VALUE;
468             }
469         }
470         ++p;
471         if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
472             return NAN_VALUE;
473         }
474         return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
475     }
476 
477     // 4. get number radix
478     bool leadingZero = false;
479     bool prefixRadix = false;
480     if (*p == '0' && radix == 0) {
481         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
482         if (*p == 'x' || *p == 'X') {
483             if ((flags & ALLOW_HEX) == 0) {
484                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
485             }
486             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
487             if (sign != Sign::NONE) {
488                 return NAN_VALUE;
489             }
490             prefixRadix = true;
491             radix = HEXADECIMAL;
492         } else if (*p == 'o' || *p == 'O') {
493             if ((flags & ALLOW_OCTAL) == 0) {
494                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
495             }
496             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
497             if (sign != Sign::NONE) {
498                 return NAN_VALUE;
499             }
500             prefixRadix = true;
501             radix = OCTAL;
502         } else if (*p == 'b' || *p == 'B') {
503             if ((flags & ALLOW_BINARY) == 0) {
504                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
505             }
506             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
507             if (sign != Sign::NONE) {
508                 return NAN_VALUE;
509             }
510             prefixRadix = true;
511             radix = BINARY;
512         } else {
513             leadingZero = true;
514         }
515     }
516 
517     if (radix == 0) {
518         radix = DECIMAL;
519     }
520     auto pStart = p;
521     // 5. skip leading '0'
522     while (*p == '0') {
523         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
524         leadingZero = true;
525     }
526     // 6. parse to number
527     uint64_t intNumber = 0;
528     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
529     int digits = 0;
530     int exponent = 0;
531     do {
532         uint8_t c = ToDigit(*p);
533         if (c >= radix) {
534             if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
535                 break;
536             }
537             // "0b" "0x1.2" "0b1e2" ...
538             return NAN_VALUE;
539         }
540         ++digits;
541         if (intNumber < numberMax) {
542             intNumber = intNumber * radix + c;
543         } else {
544             ++exponent;
545         }
546     } while (++p != end);
547 
548     auto number = static_cast<double>(intNumber);
549     if (sign == Sign::NEG) {
550         if (number == 0) {
551             number = -0.0;
552         } else {
553             number = -number;
554         }
555     }
556 
557     // 7. deal with other radix except DECIMAL
558     if (p == end || radix != DECIMAL) {
559         if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
560             // no digits there, like "0x", "0xh", or error trailing of "0x3q"
561             return NAN_VALUE;
562         }
563         return number * std::pow(radix, exponent);
564     }
565 
566     // 8. parse '.'
567     if (radix == DECIMAL && *p == '.') {
568         RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
569                                            (number * std::pow(radix, exponent)) : NAN_VALUE);
570         while (ToDigit(*p) < radix) {
571             --exponent;
572             ++digits;
573             if (++p == end) {
574                 break;
575             }
576         }
577     }
578     if (digits == 0 && !leadingZero) {
579         // no digits there, like ".", "sss", or ".e1"
580         return NAN_VALUE;
581     }
582     auto pEnd = p;
583 
584     // 9. parse 'e/E' with '+/-'
585     char exponentSign = '+';
586     int additionalExponent = 0;
587     constexpr int MAX_EXPONENT = INT32_MAX / 2;
588     if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
589         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
590 
591         // 10. parse exponent number
592         if (*p == '+' || *p == '-') {
593             exponentSign = static_cast<char>(*p);
594             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
595         }
596         uint8_t digit;
597         while ((digit = ToDigit(*p)) < radix) {
598             if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
599                 additionalExponent = MAX_EXPONENT;
600             } else {
601                 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
602             }
603             if (++p == end) {
604                 break;
605             }
606         }
607     }
608     exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
609     if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
610         return NAN_VALUE;
611     }
612 
613     // 10. build StringNumericLiteral string
614     CString buffer;
615     if (sign == Sign::NEG) {
616         buffer += "-";
617     }
618     for (uint8_t *i = pStart; i < pEnd; ++i) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
619         if (*i != static_cast<uint8_t>('.')) {
620             buffer += *i;
621         }
622     }
623 
624     // 11. convert none-prefix radix string
625     return Strtod(buffer.c_str(), exponent, radix);
626 }
627 
Strtod(const char * str,int exponent,uint8_t radix)628 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
629 {
630     ASSERT(str != nullptr);
631     ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
632     auto p = const_cast<char *>(str);
633     Sign sign = Sign::NONE;
634     uint64_t number = 0;
635     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
636     double result = 0.0;
637     if (*p == '-') {
638         sign = Sign::NEG;
639         ++p;
640     }
641     while (*p == '0') {
642         ++p;
643     }
644     while (*p != '\0') {
645         uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
646         if (digit >= radix) {
647             break;
648         }
649         if (number < numberMax) {
650             number = number * radix + digit;
651         } else {
652             ++exponent;
653         }
654         ++p;
655     }
656     if (exponent < 0) {
657         result = number / std::pow(radix, -exponent);
658     } else {
659         result = number * std::pow(radix, exponent);
660     }
661     return sign == Sign::NEG ? -result : result;
662 }
663 
DoubleToInt(double d,size_t bits)664 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
665 {
666     int32_t ret = 0;
667     auto u64 = bit_cast<uint64_t>(d);
668     int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
669     if (exp < static_cast<int>(bits - 1)) {
670         // smaller than INT<bits>_MAX, fast conversion
671         ret = static_cast<int32_t>(d);
672     } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
673         // Still has significand bits after mod 2^<bits>
674         // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
675         uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
676                           << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
677                          (INT64_BITS - bits);
678         ret = static_cast<int32_t>(value);
679         if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
680             ret = -ret;
681         }
682     } else {
683         // No significand bits after mod 2^<bits>, contains NaN and INF
684         ret = 0;
685     }
686     return ret;
687 }
688 
DoubleInRangeInt32(double d)689 int32_t NumberHelper::DoubleInRangeInt32(double d)
690 {
691     if (d > INT_MAX) {
692         return INT_MAX;
693     }
694     if (d < INT_MIN) {
695         return INT_MIN;
696     }
697     return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
698 }
699 
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)700 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
701 {
702     auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
703     uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
704     if (strLen == 0) {
705         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
706     }
707     CVector<uint8_t> buf;
708     Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
709 
710     auto p = const_cast<uint8_t *>(str.begin());
711     auto end = str.end();
712     // 1. skip space and line terminal
713     if (!NumberHelper::GotoNonspace(&p, end)) {
714         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
715     }
716     // 2. get bigint sign
717     Sign sign = Sign::NONE;
718     if (*p == '+') {
719         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
720         sign = Sign::POS;
721     } else if (*p == '-') {
722         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
723         sign = Sign::NEG;
724     }
725     // 3. bigint not allow Infinity, decimal points, or exponents.
726     if (isalpha(*p)) {
727         return JSTaggedValue(NAN_VALUE);
728     }
729     // 4. get bigint radix
730     uint8_t radix = DECIMAL;
731     if (*p == '0') {
732         if (++p == end) {
733             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
734         }
735         if (*p == 'x' || *p == 'X') {
736             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
737             if (sign != Sign::NONE) {
738                 return JSTaggedValue(NAN_VALUE);
739             }
740             radix = HEXADECIMAL;
741         } else if (*p == 'o' || *p == 'O') {
742             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
743             if (sign != Sign::NONE) {
744                 return JSTaggedValue(NAN_VALUE);
745             }
746             radix = OCTAL;
747         } else if (*p == 'b' || *p == 'B') {
748             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
749             if (sign != Sign::NONE) {
750                 return JSTaggedValue(NAN_VALUE);
751             }
752             radix = BINARY;
753         }
754     }
755 
756     auto pStart = p;
757     // 5. skip leading '0'
758     while (*p == '0') {
759         if (++p == end) {
760             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
761         }
762     }
763     // 6. parse to bigint
764     CString buffer;
765     if (sign == Sign::NEG) {
766         buffer += "-";
767     }
768     do {
769         uint8_t c = ToDigit(*p);
770         if (c >= radix) {
771             if (pStart != p && !NumberHelper::GotoNonspace(&p, end)) {
772                 break;
773             }
774             return JSTaggedValue(NAN_VALUE);
775         }
776         buffer += *p;
777     } while (++p != end);
778     if (buffer.size() == 0) {
779         return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
780     }
781     return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
782 }
783 
GetBase(double d,int digits,int * decpt,char * buf,char * bufTmp,int size)784 void NumberHelper::GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size)
785 {
786     int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
787     if (result == -1) {
788         LOG_FULL(FATAL) << "snprintf_s failed";
789         UNREACHABLE();
790     }
791     // mantissa
792     buf[0] = bufTmp[1];
793     if (digits > 1) {
794         if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
795             LOG_FULL(FATAL) << "memcpy_s failed";
796             UNREACHABLE();
797         }
798     }
799     buf[digits + 1] = '\0';
800     // exponent
801     *decpt = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
802 }
803 
GetMinmumDigits(double d,int * decpt,char * buf)804 int NumberHelper::GetMinmumDigits(double d, int *decpt, char *buf)
805 {
806     int digits = 0;
807     char bufTmp[JS_DTOA_BUF_SIZE] = {0};
808 
809     // find the minimum amount of digits
810     int MinDigits = 1;
811     int MaxDigits = DOUBLE_MAX_PRECISION;
812     while (MinDigits < MaxDigits) {
813         digits = (MinDigits + MaxDigits) / 2;
814         GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
815         if (strtod(bufTmp, NULL) == d) {
816             // no need to keep the trailing zeros
817             while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
818                 digits--;
819             }
820             MaxDigits = digits;
821         } else {
822             MinDigits = digits + 1;
823         }
824     }
825     digits = MaxDigits;
826     GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
827 
828     return digits;
829 }
830 
XorShift64(uint64_t * pVal)831 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
832 {
833     uint64_t x = *pVal;
834     x ^= x >> RIGHT12;
835     x ^= x << LEFT25;
836     x ^= x >> RIGHT27;
837     *pVal = x;
838     return x * GET_MULTIPLY;
839 }
840 
InitRandom()841 void RandomGenerator::InitRandom()
842 {
843     struct timeval tv;
844     gettimeofday(&tv, NULL);
845     randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
846     // the state must be non zero
847     if (randomState_ == 0) {
848         randomState_ = 1;
849     }
850 }
851 
NextDouble()852 double RandomGenerator::NextDouble()
853 {
854     uint64_t val = XorShift64(&randomState_);
855     return ToDouble(val);
856 }
857 
ToDouble(uint64_t state)858 double RandomGenerator::ToDouble(uint64_t state)
859 {
860     uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
861     return base::bit_cast<double>(random) - 1;
862 }
863 
Next(int bits)864 int32_t RandomGenerator::Next(int bits)
865 {
866     uint64_t val = XorShift64(&randomState_);
867     return static_cast<int32_t>(val >> (INT64_BITS - bits));
868 }
869 
GenerateIdentityHash()870 int32_t RandomGenerator::GenerateIdentityHash()
871 {
872     return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
873 }
874 }  // namespace panda::ecmascript::base
875