• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ecmascript/base/number_helper.h"
17 
18 #include <cfenv>
19 
20 #include "ecmascript/base/dtoa_helper.h"
21 #include "ecmascript/base/string_helper.h"
22 #include "ecmascript/builtins/builtins_number.h"
23 #include "ecmascript/ecma_string_table.h"
24 #include "ecmascript/js_tagged_value-inl.h"
25 
26 namespace panda::ecmascript::base {
27 using NumberToStringResultCache = builtins::NumberToStringResultCache;
28 
29 enum class Sign { NONE, NEG, POS };
30 thread_local uint64_t RandomGenerator::randomState_ {0};
31 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
32 #define RETURN_IF_CONVERSION_END(p, end, result) \
33     if ((p) == (end)) {                          \
34         return (result);                         \
35     }
36 
37 constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz";  // NOLINT (modernize-avoid-c-arrays)
38 constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
39 
40 static const double POWERS_OF_TEN[] = {
41     1.0,                      // 10^0
42     10.0,
43     100.0,
44     1000.0,
45     10000.0,
46     100000.0,
47     1000000.0,
48     10000000.0,
49     100000000.0,
50     1000000000.0,
51     10000000000.0,            // 10^10
52     100000000000.0,
53     1000000000000.0,
54     10000000000000.0,
55     100000000000000.0,
56     1000000000000000.0,
57     10000000000000000.0,
58     100000000000000000.0,
59     1000000000000000000.0,
60     10000000000000000000.0,
61     100000000000000000000.0,  // 10^20
62     1000000000000000000000.0,
63     10000000000000000000000.0 // 10^22
64 };
65 static const int POWERS_OF_TEN_SIZE = 23;
66 const CString NumberHelper::NAN_STR = "NaN";
67 const CString NumberHelper::ZERO_STR = "0";
68 const CString NumberHelper::MINUS_INFINITY_STR = "-Infinity";
69 const CString NumberHelper::INFINITY_STR = "Infinity";
70 
ToDigit(uint8_t c)71 static inline uint8_t ToDigit(uint8_t c)
72 {
73     if (c >= '0' && c <= '9') {
74         return c - '0';
75     }
76     if (c >= 'A' && c <= 'Z') {
77         return c - 'A' + DECIMAL;
78     }
79     if (c >= 'a' && c <= 'z') {
80         return c - 'a' + DECIMAL;
81     }
82     return '$';
83 }
84 
GotoNonspace(uint8_t ** ptr,const uint8_t * end)85 bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
86 {
87     while (*ptr < end) {
88         uint16_t c = **ptr;
89         size_t size = 1;
90         if (c > INT8_MAX) {
91             size = 0;
92             uint16_t utf8Bit = INT8_MAX + 1;  // equal 0b1000'0000
93             while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
94                 ++size;
95                 utf8Bit >>= 1UL;
96             }
97             if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1) <= 0) {
98                 return true;
99             }
100         }
101         if (!StringHelper::IsNonspace(c)) {
102             return true;
103         }
104         *ptr += size;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
105     }
106     return false;
107 }
108 
SignedZero(Sign sign)109 static inline double SignedZero(Sign sign)
110 {
111     return sign == Sign::NEG ? -0.0 : 0.0;
112 }
113 
IsEmptyString(const uint8_t * start,const uint8_t * end)114 bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
115 {
116     auto p = const_cast<uint8_t *>(start);
117     return !NumberHelper::GotoNonspace(&p, end);
118 }
119 
120 /*
121 *  This Function Translate from number 0-9 to number '0'-'9'
122 *                               number 10-35 to number 'a'-'z'
123 */
ToCharCode(uint32_t number)124 uint32_t NumberHelper::ToCharCode(uint32_t number)
125 {
126     ASSERT(number < 36); // 36 == total number of '0'-'9' + 'a' -'z'
127     return number < 10 ? (number + 48): // 48 == '0'; 10: '0' - '9';
128                          (number - 10 + 97); // 97 == 'a'; 'a' - 'z'
129 }
130 
Int32ToString(JSThread * thread,int32_t number,uint32_t radix)131 JSTaggedValue NumberHelper::Int32ToString(JSThread *thread, int32_t number, uint32_t radix)
132 {
133     bool isNegative = number < 0;
134     uint32_t n = 0;
135     if (!isNegative) {
136         n = static_cast<uint32_t>(number);
137         if (n < radix) {
138             if (n == 0) {
139                 return thread->GlobalConstants()->GetHandledZeroString().GetTaggedValue();
140             }
141             JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
142             return singleCharTable->GetStringFromSingleCharTable(ToCharCode(n));
143         }
144     } else {
145         n = static_cast<uint32_t>(-number);
146     }
147     uint32_t temp = n;
148     uint32_t length = isNegative ? 1 : 0;
149     // calculate length
150     while (temp > 0) {
151         temp = temp / radix;
152         length = length + 1;
153     }
154     std::string buf;
155     buf.resize(length);
156     ASSERT(length > 0);
157     uint32_t index = length - 1;
158     uint32_t digit = 0;
159     while (n > 0) {
160         digit = n % radix;
161         n /= radix;
162         buf[index] = ToCharCode(digit) + 0X00;
163         index--;
164     }
165     if (isNegative) {
166         ASSERT(index == 0);
167         buf[index] = '-';
168     }
169     return thread->GetEcmaVM()->GetFactory()->NewFromUtf8(buf).GetTaggedValue();
170 }
171 
DoubleToString(JSThread * thread,double number,int radix)172 JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
173 {
174     static constexpr int BUFFER_SIZE = 2240; // 2240: The size of the character array buffer
175     static constexpr int HALF_BUFFER_SIZE = BUFFER_SIZE >> 1;
176     char buffer[BUFFER_SIZE];
177     size_t integerCursor = HALF_BUFFER_SIZE;
178     size_t fractionCursor = integerCursor;
179 
180     bool negative = number < 0.0;
181     if (negative) {
182         number = -number;
183     }
184 
185     double integer = std::floor(number);
186     double fraction = number - integer;
187 
188     auto value = bit_cast<uint64_t>(number);
189     value += 1;
190     double delta = HALF * (bit_cast<double>(value) - number);
191     delta = std::max(delta, bit_cast<double>(static_cast<uint64_t>(1))); // 1 : The binary of the smallest double is 1
192     if (fraction != 0 && fraction >= delta) {
193         buffer[fractionCursor++] = '.';
194         while (fraction >= delta) {
195             fraction *= radix;
196             delta *= radix;
197             int64_t digit = std::floor(fraction);
198             fraction -= digit;
199             buffer[fractionCursor++] = CHARS[digit];
200             bool needCarry = (fraction > HALF) && (fraction + delta > 1);
201             if (needCarry) {
202                 size_t fractionEnd = fractionCursor - 1;
203                 buffer[fractionEnd] = Carry(buffer[fractionEnd], radix);
204                 for (; fractionEnd > HALF_BUFFER_SIZE; fractionEnd--) {
205                     if (buffer[fractionEnd] == '0') {
206                         buffer[fractionEnd - 1] = Carry(buffer[fractionEnd - 1], radix);
207                     } else {
208                         break;
209                     }
210                 }
211                 if (fractionEnd == HALF_BUFFER_SIZE) {
212                     ++integer;
213                 }
214                 break;
215             }
216         }
217         // delete 0 in the end
218         size_t fractionEnd = fractionCursor - 1;
219         while (buffer[fractionEnd] == '0') {
220             --fractionEnd;
221         }
222         fractionCursor = fractionEnd + 1;
223     }
224 
225     ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
226     while (Exponent(integer / radix) > 0) {
227         integer /= radix;
228         buffer[--integerCursor] = '0';
229     }
230     do {
231         double remainder = std::fmod(integer, radix);
232         buffer[--integerCursor] = CHARS[static_cast<int>(remainder)];
233         integer = (integer - remainder) / radix;
234     } while (integer > 0);
235 
236     if (negative) {
237         buffer[--integerCursor] = '-';
238     }
239     buffer[fractionCursor++] = '\0';
240 
241     size_t size = fractionCursor - integerCursor;
242     std::unique_ptr<char[]> result = std::make_unique<char[]>(size);
243     if (memcpy_s(result.get(), size, buffer + integerCursor, size) != EOK) {
244         LOG_FULL(FATAL) << "memcpy_s failed";
245         UNREACHABLE();
246     }
247     return BuiltinsBase::GetTaggedString(thread, result.get());
248 }
249 
DoubleToFixedString(JSThread * thread,double valueNumber,int digitNumber)250 JSTaggedValue NumberHelper::DoubleToFixedString(JSThread *thread, double valueNumber, int digitNumber)
251 {
252     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
253     bool negative = false;
254     double absValue = valueNumber;
255     std::string result;
256 
257     if (valueNumber < 0) {
258         result += "-";
259         absValue = -valueNumber;
260         negative = true;
261     }
262     int decimalPoint;
263     const int decimalRepCapacity = MAX_DIGITS + MAX_FRACTION + 1; // Add space for the '\0' byte.
264     char decimalRep[decimalRepCapacity];
265     int length;
266     bool isFast = DtoaHelper::FixedDtoa(absValue, digitNumber,
267         BufferVector<char>(decimalRep, decimalRepCapacity), &length, &decimalPoint);
268     if (!isFast) {
269         return DoubleToASCII(thread, valueNumber, digitNumber, base::FRAC_FORMAT); // slow
270     }
271     int zeroPrefixLen = 0;
272     int zeroPostfixLen = 0;
273     if (decimalPoint <= 0) {
274         zeroPrefixLen = -decimalPoint + 1;
275         decimalPoint = 1;
276     }
277     if (zeroPrefixLen + length < decimalPoint + digitNumber) {
278         zeroPostfixLen = decimalPoint + digitNumber - length - zeroPrefixLen;
279     }
280     result += std::string(zeroPrefixLen, '0');
281     result += decimalRep;
282     result += std::string(zeroPostfixLen, '0');
283     if (digitNumber > 0) {
284         if (negative) {
285             result.insert(decimalPoint + 1, 1, '.');
286         } else {
287             result.insert(decimalPoint, 1, '.');
288         }
289     }
290     return factory->NewFromASCII(result.c_str()).GetTaggedValue();
291 }
292 
DoubleToASCII(JSThread * thread,double valueNumber,int digitNumber,int flags)293 JSTaggedValue NumberHelper::DoubleToASCII(JSThread *thread, double valueNumber, int digitNumber, int flags)
294 {
295     std::string buffer(JS_DTOA_BUF_SIZE, '\0');
296     DoubleToASCIIWithFlag(buffer, valueNumber, digitNumber, flags);
297     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
298     return factory->NewFromASCII(buffer.c_str()).GetTaggedValue();
299 }
300 
GetBaseForRoundingMode(double valueNumber,int digitNumber,int * decimalPoint,std::string & buf,std::string & buf1,int buf1Size,int roundingMode,int * sign)301 void NumberHelper::GetBaseForRoundingMode(double valueNumber, int digitNumber, int *decimalPoint, std::string& buf,
302                                           std::string& buf1, int buf1Size, int roundingMode, int *sign)
303 {
304     if (roundingMode != FE_TONEAREST) {
305         fesetround(roundingMode);
306     }
307     int result = snprintf_s(&buf1[0], buf1Size, buf1Size - 1, "%+.*e", digitNumber - 1, valueNumber);
308     if (result == -1) {
309         LOG_FULL(FATAL) << "snprintf_s failed";
310         UNREACHABLE();
311     }
312     if (roundingMode != FE_TONEAREST) {
313         fesetround(FE_TONEAREST);
314     }
315     *sign = (buf1[0] == '-');
316     buf[0] = buf1[1];
317     if (digitNumber > 1) {
318         if (memcpy_s(&buf[1], digitNumber - 1, &buf1[POINT_INDEX], digitNumber - 1) != EOK) {
319             LOG_FULL(FATAL) << "memcpy_s failed";
320             UNREACHABLE();
321         }
322     }
323     buf[digitNumber] = '\0';
324     *decimalPoint = std::atoi(&buf1[digitNumber + DECIMAL_INDEX + (digitNumber > 1)]) + 1;
325 }
326 
CustomEcvtIsFixed(double & valueNumber,int & digits,int * decimalPoint,std::string & buf,int * sign)327 void NumberHelper::CustomEcvtIsFixed(double &valueNumber, int &digits, int *decimalPoint, std::string& buf, int *sign)
328 {
329     std::string buffer(JS_DTOA_BUF_SIZE, '\0');
330     unsigned int digitsMin = 1;
331     unsigned int digitsMax = DOUBLE_MAX_PRECISION;
332     while (digitsMin < digitsMax) {
333         digits = (digitsMin + digitsMax) / MIN_RADIX;
334         ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
335         GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, FE_TONEAREST, sign);
336         if (std::strtod(buffer.c_str(), NULL) == valueNumber) {
337             while (digits >= MIN_RADIX && buf[digits - 1] == '0') {
338                 digits--;
339             }
340             digitsMax = static_cast<unsigned int>(digits);
341         } else {
342             digitsMin = static_cast<unsigned int>(digits) + 1;
343         }
344     }
345     digits = static_cast<int>(digitsMax);
346 }
347 
CustomEcvt(double valueNumber,int digits,int * decimalPoint,std::string & buf,bool isFixed,int * sign)348 int NumberHelper::CustomEcvt(double valueNumber, int digits, int *decimalPoint,
349                              std::string& buf, bool isFixed, int *sign)
350 {
351     std::string buffer(JS_DTOA_BUF_SIZE, '\0');
352     int roundingMode = FE_TONEAREST;
353     if (!isFixed) {
354         CustomEcvtIsFixed(valueNumber, digits, decimalPoint, buf, sign);
355     } else {
356         std::string buf1(JS_DTOA_BUF_SIZE, '\0');
357         std::string buf2(JS_DTOA_BUF_SIZE, '\0');
358         int decpt1 = 0;
359         int decpt2 = 0;
360         int sign1 = 0;
361         int sign2 = 0;
362         ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
363         GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE, roundingMode, &sign1);
364         if (buf1[digits] == HALFCHAR) {
365             ASSERT(buf1.size() <= JS_DTOA_BUF_SIZE);
366             GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE,
367                 FE_DOWNWARD, &sign1);
368             ASSERT(buf2.size() <= JS_DTOA_BUF_SIZE);
369             GetBaseForRoundingMode(valueNumber, digits + 1, &decpt2, buf2, buffer, JS_DTOA_BUF_SIZE,
370                 FE_UPWARD, &sign2);
371             if (memcmp(buf1.c_str(), buf2.c_str(), digits + 1) == 0 && decpt1 == decpt2) {
372                 roundingMode = sign1 ? FE_DOWNWARD : FE_UPWARD;
373             }
374         }
375     }
376     ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
377     GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, roundingMode, sign);
378     return digits;
379 }
380 
CustomFcvtHelper(std::string & buf,int bufSize,double valueNumber,int digits,int roundingMode)381 int NumberHelper::CustomFcvtHelper(std::string& buf, int bufSize, double valueNumber, int digits, int roundingMode)
382 {
383     if (roundingMode != FE_TONEAREST) {
384         std::fesetround(roundingMode);
385     }
386     int result = snprintf_s(&buf[0], bufSize, bufSize, "%.*f", digits, valueNumber);
387     if (result == -1) {
388         LOG_FULL(FATAL) << "snprintf_s failed";
389         UNREACHABLE();
390     }
391     if (roundingMode != FE_TONEAREST) {
392         std::fesetround(FE_TONEAREST);
393     }
394     ASSERT(result < bufSize);
395     return result;
396 }
397 
CustomFcvt(std::string & buf,int bufSize,double valueNumber,int digits)398 void NumberHelper::CustomFcvt(std::string& buf, int bufSize, double valueNumber, int digits)
399 {
400     int number = 0;
401     int tmpNumber = 0;
402     std::string tmpbuf1(JS_DTOA_BUF_SIZE, '\0');
403     std::string tmpbuf2(JS_DTOA_BUF_SIZE, '\0');
404     int roundingMode = FE_TONEAREST;
405     number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, roundingMode);
406     if (tmpbuf1[number - 1] == HALFCHAR) {
407         number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_DOWNWARD);
408         tmpNumber = CustomFcvtHelper(tmpbuf2, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_UPWARD);
409         if (tmpbuf1 == tmpbuf2) {
410             if (tmpbuf1[0] == '-') {
411                 roundingMode = FE_DOWNWARD;
412             } else {
413                 roundingMode = FE_UPWARD;
414             }
415         }
416     }
417     CustomFcvtHelper(buf, bufSize, valueNumber, digits, roundingMode);
418 }
419 
DoubleToPrecisionString(JSThread * thread,double number,int digit)420 JSTaggedValue NumberHelper::DoubleToPrecisionString(JSThread *thread, double number, int digit)
421 {
422     if (number == 0.0) {
423         return DoubleToFixedString(thread, number, digit - 1);
424     }
425     double positiveNumber = number > 0 ? number : -number;
426     int logDigit = std::floor(log10(positiveNumber));
427     int radixDigit = digit - logDigit - 1;
428     const int MIN_EXPONENT_DIGIT = -6;
429     if ((logDigit >= MIN_EXPONENT_DIGIT && logDigit < digit)) {
430         return DoubleToFixedString(thread, number, std::abs(radixDigit));
431     }
432     return DoubleToExponential(thread, number, digit);
433 }
434 
DoubleToExponential(JSThread * thread,double number,int digit)435 JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
436 {
437     char tmpbuf[JS_DTOA_BUF_SIZE] = {0};
438     // Can use std::to_chars for performance.
439     if (digit == 0) {
440         if (number == 0.0) {
441             return BuiltinsBase::GetTaggedString(thread, "0e+0");
442         }
443         std::string res;
444         if (number < 0) {
445             res += "-";
446             number = -number;
447         }
448         int n;
449         int k;
450         DtoaHelper::Dtoa(number, tmpbuf, &n, &k);
451         std::string base = tmpbuf;
452         base.erase(1, k - 1);
453         if (k != 1) {
454             base += std::string(".") + std::string(tmpbuf + 1);
455         }
456         base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
457         res += base;
458         return BuiltinsBase::GetTaggedString(thread, res.c_str());
459     } else {
460         int result = snprintf_s(tmpbuf, sizeof(tmpbuf), sizeof(tmpbuf) - 1, "%.*e", digit - 1, number);
461         if (result == -1) {
462             LOG_FULL(FATAL) << "snprintf_s failed";
463             UNREACHABLE();
464         }
465     }
466     std::string result = tmpbuf;
467     size_t found = result.find_last_of('e');
468     if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') { // 2:offset of e
469         result.erase(found + 2, 1); // 2:offset of e
470     }
471     if (digit < 0) {
472         size_t end = found;
473         while (--found > 0) {
474             if (result[found] != '0') {
475                 break;
476             }
477         }
478         if (result[found] == '.') {
479             found--;
480         }
481         if (found < end - 1) {
482             result.erase(found + 1, end - found - 1);
483         }
484     }
485     return BuiltinsBase::GetTaggedString(thread, result.c_str());
486 }
487 
DoubleToASCIIWithFlag(std::string & buf,double valueNumber,int digits,int flags)488 void NumberHelper::DoubleToASCIIWithFlag(std::string& buf, double valueNumber, int digits, int flags)
489 {
490     if (valueNumber == 0.0) {
491         valueNumber = 0.0;
492     }
493     if (flags == FRAC_FORMAT) {
494         CustomFcvt(buf, JS_DTOA_BUF_SIZE, valueNumber, digits);
495     } else {
496         std::string buf1(JS_DTOA_BUF_SIZE, '\0');
497         int decimalPoint = 0;
498         int sign = 0;
499         bool fixed = ((static_cast<unsigned int>(flags) & POINT_INDEX) ==
500             static_cast<unsigned int>(base::FIXED_FORMAT));
501         int numberMax = fixed ? digits : MAX_DIGITS;
502         int digitNumber = CustomEcvt(valueNumber, digits, &decimalPoint, buf1, fixed, &sign);
503         int number = decimalPoint;
504         std::string tmpbuf;
505         int i = 0;
506         if (sign) {
507             tmpbuf += '-';
508         }
509         if (number > 0 && number <= numberMax) {
510             ToASCIIWithGreatThanZero(tmpbuf, digitNumber, number, buf1);
511         } else if (MIN_DIGITS < number && number <= 0) {
512             tmpbuf += '0';
513             tmpbuf += '.';
514             for (i = 0; i < -number; i++) {
515                 tmpbuf += '0';
516             }
517             tmpbuf += buf1.substr(0, digitNumber);
518         } else {
519             ToASCIIWithNegative(tmpbuf, digitNumber, number, buf1);
520         }
521         buf = tmpbuf;
522     }
523 }
524 
ToASCIIWithGreatThanZero(std::string & tmpbuf,int digitNumber,int number,const std::string & buf)525 void NumberHelper::ToASCIIWithGreatThanZero(std::string& tmpbuf, int digitNumber, int number, const std::string& buf)
526 {
527     if (digitNumber <= number) {
528         tmpbuf += buf.substr(0, digitNumber);
529         tmpbuf += std::string(number - digitNumber, '0');
530         tmpbuf += '\0';
531     } else {
532         tmpbuf += buf.substr(0, number);
533         tmpbuf += '.';
534         tmpbuf += buf.substr(number, digitNumber - number);
535         tmpbuf += '\0';
536     }
537 }
538 
ToASCIIWithNegative(std::string & tmpbuf,int digitNumber,int n,const std::string & buf)539 void NumberHelper::ToASCIIWithNegative(std::string& tmpbuf, int digitNumber, int n, const std::string& buf)
540 {
541     tmpbuf += buf[0];
542     if (digitNumber > 1) {
543         tmpbuf += '.';
544         for (int i = 1; i < digitNumber; i++) {
545             tmpbuf += buf[i];
546         }
547     }
548     tmpbuf += 'e';
549     int p = n - 1;
550     if (p >= 0) {
551         tmpbuf += '+';
552     }
553     tmpbuf += std::to_string(p);
554 }
555 
StringToNumber(EcmaString * string,int32_t radix)556 JSTaggedValue NumberHelper::StringToNumber(EcmaString *string, int32_t radix)
557 {
558     bool negative = false;
559     if ((radix == base::DECIMAL || radix == 0)) {
560         int32_t elementIndex = 0;
561         if (EcmaStringAccessor(string).ToInt(&elementIndex, &negative)) {
562             if (elementIndex == 0 && negative == true) {
563                 return JSTaggedValue(-0.0);
564             }
565             return JSTaggedValue(elementIndex);
566         }
567     }
568     CVector<uint8_t> buf;
569     Span<const uint8_t> str = EcmaStringAccessor(string).ToUtf8Span(buf);
570 
571     JSTaggedValue result = NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix, &negative);
572     if (result.GetNumber() == 0 && negative == true) {
573         return JSTaggedValue(-0.0);
574     }
575     return JSTaggedValue::TryCastDoubleToInt32(result.GetNumber());
576 }
577 
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix,bool * negative)578 JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix, bool *negative)
579 {
580     auto p = const_cast<uint8_t *>(start);
581     JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
582     // 1. skip space and line terminal
583     if (!NumberHelper::GotoNonspace(&p, end)) {
584         return nanResult;
585     }
586 
587     // 2. sign bit
588     if (*p == '-') {
589         *negative = true;
590         RETURN_IF_CONVERSION_END(++p, end, nanResult);
591     } else if (*p == '+') {
592         RETURN_IF_CONVERSION_END(++p, end, nanResult);
593     }
594     // 3. 0x or 0X
595     bool stripPrefix = true;
596     // 4. If R  0, then
597     //     a. If R < 2 or R > 36, return NaN.
598     //     b. If R  16, let stripPrefix be false.
599     if (radix != 0) {
600         if (radix < MIN_RADIX || radix > MAX_RADIX) {
601             return nanResult;
602         }
603         if (radix != HEXADECIMAL) {
604             stripPrefix = false;
605         }
606     } else {
607         radix = DECIMAL;
608     }
609     int size = 0;
610     if (stripPrefix) {
611         if (*p == '0') {
612             if (++p != end && (*p == 'x' || *p == 'X')) {
613                 RETURN_IF_CONVERSION_END(++p, end, nanResult);
614                 radix = HEXADECIMAL;
615             } else {
616                 size++;
617             }
618         }
619     }
620 
621     double result = 0;
622     bool isDone = false;
623     do {
624         double part = 0;
625         uint32_t multiplier = 1;
626         for (; p != end; ++p) {
627             // The maximum value to ensure that uint32_t will not overflow
628             const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
629             uint32_t m = multiplier * static_cast<uint32_t>(radix);
630             if (m > MAX_MULTIPER) {
631                 break;
632             }
633 
634             int currentBit = static_cast<int>(ToDigit(*p));
635             if (currentBit >= radix) {
636                 isDone = true;
637                 break;
638             }
639             size++;
640             part = part * radix + currentBit;
641             multiplier = m;
642         }
643         result = result * multiplier + part;
644         if (isDone) {
645             break;
646         }
647     } while (p != end);
648 
649     if (size == 0) {
650         return nanResult;
651     }
652 
653     if (*negative) {
654         result = -result;
655     }
656     return BuiltinsBase::GetTaggedDouble(result);
657 }
658 
Carry(char current,int radix)659 char NumberHelper::Carry(char current, int radix)
660 {
661     int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
662     digit = (digit == (radix - 1)) ? 0 : digit + 1;
663     return CHARS[digit];
664 }
665 
IntegerToString(double number,int radix)666 CString NumberHelper::IntegerToString(double number, int radix)
667 {
668     ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
669     CString result;
670     while (number / radix > MAX_MANTISSA) {
671         number /= radix;
672         result = CString("0").append(result);
673     }
674     do {
675         double remainder = std::fmod(number, radix);
676         result = CHARS[static_cast<int>(remainder)] + result;
677         number = (number - remainder) / radix;
678     } while (number > 0);
679     return result;
680 }
681 
IntToString(int number)682 CString NumberHelper::IntToString(int number)
683 {
684     return ToCString(number);
685 }
686 
AppendIntToString(CString & str,int number)687 void NumberHelper::AppendIntToString(CString &str, int number)
688 {
689     return AppendToCString(str, number);
690 }
691 
IntToEcmaString(const JSThread * thread,int number)692 JSHandle<EcmaString> NumberHelper::IntToEcmaString(const JSThread *thread, int number)
693 {
694     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
695     return factory->NewFromASCII(ToCString(number));
696 }
697 
DoubleToCString(double d)698 CString NumberHelper::DoubleToCString(double d)
699 {
700     if (std::isnan(d)) {
701         return NumberHelper::NAN_STR;
702     }
703     if (d == 0.0) {
704         return NumberHelper::ZERO_STR;
705     }
706     if (std::isinf(d)) {
707         if (d < 0) {
708             return NumberHelper::MINUS_INFINITY_STR;
709         } else {
710             return NumberHelper::INFINITY_STR;
711         }
712     }
713     bool isNeg = false;
714     if (d < 0) {
715         isNeg = true;
716         d = -d;
717     }
718     ASSERT(d > 0);
719     constexpr int startIdx = 8;
720     char buff[JS_DTOA_BUF_SIZE] = {0};
721     char *result = buff + startIdx;
722     int n; // decimal point
723     int k; // length
724     DtoaHelper::Dtoa(d, result, &n, &k); //Fast Double To Ascii.
725     if (n > 0 && n <= MAX_DIGITS) {
726         if (k <= n) {
727             // 6. If k ≤ n ≤ 21
728             for (int i = k; i < n; ++i) {
729                 result[i] = '0';
730             }
731         } else {
732             // 7. If 0 < n ≤ 21
733             --result;
734             for (int i = 0; i < n; ++i) {
735                 result[i] = result[i + 1];
736             }
737             result[n] = '.';
738         }
739     } else if (MIN_DIGITS < n && n <= 0) {
740         // 8. If −6 < n ≤ 0
741         constexpr int prefixLen = 2;
742         result -= (-n + prefixLen);
743         result[0] = '0';
744         result[1] = '.';
745         for (int i = prefixLen; i < -n + prefixLen; ++i) {
746             result[i] = '0';
747         }
748     } else {
749         // 9. & 10. Otherwise
750         int pos = k;
751         if (k != 1) {
752             --result;
753             result[0] = result[1];
754             result[1] = '.';
755             ++pos;
756         }
757         result[pos++] = 'e';
758         if (n >= 1) {
759             result[pos++] = '+';
760         }
761         auto expo = std::to_string(n - 1);
762         auto p = expo.c_str();
763         for (size_t i = 0; i < expo.length(); ++i) {
764             result[pos++] = p[i];
765         }
766     }
767     if (isNeg) {
768         --result;
769         result[0] = '-';
770     }
771     return result;
772 }
773 
774 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)775 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
776 {
777     ASSERT(number.IsNumber());
778     JSHandle<NumberToStringResultCache> cacheTable(
779         thread->GetCurrentEcmaContext()->GetNumberToStringResultCache());
780     int entry = cacheTable->GetNumberHash(number);
781     JSTaggedValue cacheResult = cacheTable->FindCachedResult(entry, number);
782     if (cacheResult != JSTaggedValue::Undefined()) {
783         return JSHandle<EcmaString>::Cast(JSHandle<JSTaggedValue>(thread, cacheResult));
784     }
785 
786     JSHandle<EcmaString> resultJSHandle;
787     if (number.IsInt()) {
788         int intVal = number.GetInt();
789         if (intVal == 0) {
790             resultJSHandle = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
791         } else {
792             resultJSHandle = IntToEcmaString(thread, intVal);
793         }
794     } else {
795         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
796         resultJSHandle = factory->NewFromASCIISkippingStringTable(DoubleToCString(number.GetDouble()));
797     }
798 
799     cacheTable->SetCachedResult(thread, entry, number, resultJSHandle);
800     return resultJSHandle;
801 }
802 
TruncateDouble(double d)803 double NumberHelper::TruncateDouble(double d)
804 {
805     if (std::isnan(d)) {
806         return 0;
807     }
808     if (!std::isfinite(d)) {
809         return d;
810     }
811     // -0 to +0
812     if (d == 0.0) {
813         return 0;
814     }
815     double ret = (d >= 0) ? std::floor(d) : std::ceil(d);
816     if (ret == 0.0) {
817         ret = 0;
818     }
819     return ret;
820 }
821 
DoubleToInt64(double d)822 int64_t NumberHelper::DoubleToInt64(double d)
823 {
824     if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
825         return std::numeric_limits<int64_t>::max();
826     }
827     if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
828         return std::numeric_limits<int64_t>::min();
829     }
830     return static_cast<int64_t>(d);
831 }
832 
DoubleToUInt64(double d)833 uint64_t NumberHelper::DoubleToUInt64(double d)
834 {
835     ASSERT(d <= static_cast<double>(std::numeric_limits<uint64_t>::max()) &&
836            d >= static_cast<double>(std::numeric_limits<uint64_t>::min()));
837     return static_cast<uint64_t>(d);
838 }
839 
IsDigitalString(const uint8_t * start,const uint8_t * end)840 bool NumberHelper::IsDigitalString(const uint8_t *start, const uint8_t *end)
841 {
842     int len = end - start;
843     for (int i = 0; i < len; i++) {
844         if (*(start + i) < '0' || *(start + i) > '9') {
845             return false;
846         }
847     }
848     return true;
849 }
850 
StringToInt(const uint8_t * start,const uint8_t * end)851 int NumberHelper::StringToInt(const uint8_t *start, const uint8_t *end)
852 {
853     int num = *start - '0';
854     for (int i = 1; i < (end - start); i++) {
855         num = 10 * num + (*(start + i) - '0'); // 10 : 10 represents the base of the decimal system
856     }
857     return num;
858 }
859 
860 // only for string is ordinary string and using UTF8 encoding
861 // Fast path for short integer and some special value
FastStringToNumber(const uint8_t * start,const uint8_t * end,JSTaggedValue string)862 std::pair<bool, JSTaggedNumber> NumberHelper::FastStringToNumber(const uint8_t *start,
863     const uint8_t *end, JSTaggedValue string)
864 {
865     ASSERT(start < end);
866     EcmaStringAccessor strAccessor(string);
867     bool minus = (start[0] == '-');
868     int pos = (minus ? 1 : 0);
869 
870     if (pos == (end - start)) {
871         return {true, JSTaggedNumber(NAN_VALUE)};
872     } else if (*(start + pos) > '9') {
873         // valid number's codes not longer than '9', except 'I' and non-breaking space.
874         if (*(start + pos) != 'I' && *(start + pos) != 0xA0) {
875             return {true, JSTaggedNumber(NAN_VALUE)};
876         }
877     } else if ((end - (start + pos)) <= MAX_ELEMENT_INDEX_LEN && IsDigitalString((start + pos), end)) {
878         int num = StringToInt((start + pos), end);
879         if (minus) {
880             if (num == 0) {
881                 return {true, JSTaggedNumber(SignedZero(Sign::NEG))};
882             }
883             num = -num;
884         } else {
885             if ((num != 0) || (end - start == 1)) {
886                 strAccessor.TryToSetIntegerHash(num);
887             }
888         }
889         return {true, JSTaggedNumber(num)};
890     }
891 
892     return {false, JSTaggedNumber(NAN_VALUE)};
893 }
894 
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)895 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
896 {
897     auto p = const_cast<uint8_t *>(start);
898     // 1. skip space and line terminal
899     if (!NumberHelper::GotoNonspace(&p, end)) {
900         return 0.0;
901     }
902 
903     // 2. get number sign
904     Sign sign = Sign::NONE;
905     if (*p == '+') {
906         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
907         sign = Sign::POS;
908     } else if (*p == '-') {
909         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
910         sign = Sign::NEG;
911     }
912     bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
913 
914     // 3. judge Infinity
915     static const char INF[] = "Infinity";  // NOLINT(modernize-avoid-c-arrays)
916     if (*p == INF[0]) {
917         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
918         for (const char *i = &INF[1]; *i != '\0'; ++i) {
919             if (++p == end || *p != *i) {
920                 return NAN_VALUE;
921             }
922         }
923         ++p;
924         if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
925             return NAN_VALUE;
926         }
927         return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
928     }
929 
930     // 4. get number radix
931     bool leadingZero = false;
932     bool prefixRadix = false;
933     if (*p == '0' && radix == 0) {
934         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
935         if (*p == 'x' || *p == 'X') {
936             if ((flags & ALLOW_HEX) == 0) {
937                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
938             }
939             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
940             if (sign != Sign::NONE) {
941                 return NAN_VALUE;
942             }
943             prefixRadix = true;
944             radix = HEXADECIMAL;
945         } else if (*p == 'o' || *p == 'O') {
946             if ((flags & ALLOW_OCTAL) == 0) {
947                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
948             }
949             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
950             if (sign != Sign::NONE) {
951                 return NAN_VALUE;
952             }
953             prefixRadix = true;
954             radix = OCTAL;
955         } else if (*p == 'b' || *p == 'B') {
956             if ((flags & ALLOW_BINARY) == 0) {
957                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
958             }
959             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
960             if (sign != Sign::NONE) {
961                 return NAN_VALUE;
962             }
963             prefixRadix = true;
964             radix = BINARY;
965         } else {
966             leadingZero = true;
967         }
968     }
969 
970     if (radix == 0) {
971         radix = DECIMAL;
972     }
973     auto pStart = p;
974     // 5. skip leading '0'
975     while (*p == '0') {
976         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
977         leadingZero = true;
978     }
979     // 6. parse to number
980     uint64_t intNumber = 0;
981     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
982     int digits = 0;
983     int exponent = 0;
984     do {
985         uint8_t c = ToDigit(*p);
986         if (c >= radix) {
987             if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
988                 break;
989             }
990             // "0b" "0x1.2" "0b1e2" ...
991             return NAN_VALUE;
992         }
993         ++digits;
994         if (intNumber < numberMax) {
995             intNumber = intNumber * radix + c;
996         } else {
997             ++exponent;
998         }
999     } while (++p != end);
1000 
1001     auto number = static_cast<double>(intNumber);
1002     if (sign == Sign::NEG) {
1003         if (number == 0) {
1004             number = -0.0;
1005         } else {
1006             number = -number;
1007         }
1008     }
1009 
1010     // 7. deal with other radix except DECIMAL
1011     if (p == end || radix != DECIMAL) {
1012         if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
1013             // no digits there, like "0x", "0xh", or error trailing of "0x3q"
1014             return NAN_VALUE;
1015         }
1016         return number * std::pow(radix, exponent);
1017     }
1018 
1019     // 8. parse '.'
1020     exponent = 0;
1021     if (radix == DECIMAL && *p == '.') {
1022         RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
1023                                            (number * std::pow(radix, exponent)) : NAN_VALUE);
1024         while (ToDigit(*p) < radix) {
1025             --exponent;
1026             ++digits;
1027             if (++p == end) {
1028                 break;
1029             }
1030         }
1031     }
1032     if (digits == 0 && !leadingZero) {
1033         // no digits there, like ".", "sss", or ".e1"
1034         return NAN_VALUE;
1035     }
1036     auto pEnd = p;
1037 
1038     // 9. parse 'e/E' with '+/-'
1039     char exponentSign = '+';
1040     int additionalExponent = 0;
1041     constexpr int MAX_EXPONENT = INT32_MAX / 2;
1042     if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
1043         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1044 
1045         // 10. parse exponent number
1046         if (*p == '+' || *p == '-') {
1047             exponentSign = static_cast<char>(*p);
1048             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1049         }
1050         uint8_t digit;
1051         while ((digit = ToDigit(*p)) < radix) {
1052             if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
1053                 additionalExponent = MAX_EXPONENT;
1054             } else {
1055                 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
1056             }
1057             if (++p == end) {
1058                 break;
1059             }
1060         }
1061     }
1062     exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
1063     if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
1064         return NAN_VALUE;
1065     }
1066 
1067     // 10. build StringNumericLiteral string
1068     CString buffer;
1069     if (sign == Sign::NEG) {
1070         buffer += "-";
1071     }
1072     for (uint8_t *i = pStart; i < pEnd; ++i) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1073         if (*i != static_cast<uint8_t>('.')) {
1074             buffer += *i;
1075         }
1076     }
1077 
1078     // 11. convert none-prefix radix string
1079     return Strtod(buffer.c_str(), exponent, radix);
1080 }
1081 
Strtod(const char * str,int exponent,uint8_t radix)1082 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
1083 {
1084     ASSERT(str != nullptr);
1085     ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
1086     auto p = const_cast<char *>(str);
1087     Sign sign = Sign::NONE;
1088     uint64_t number = 0;
1089     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
1090     double result = 0.0;
1091     if (*p == '-') {
1092         sign = Sign::NEG;
1093         ++p;
1094     }
1095     while (*p == '0') {
1096         ++p;
1097     }
1098     while (*p != '\0') {
1099         uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
1100         if (digit >= radix) {
1101             break;
1102         }
1103         if (number < numberMax) {
1104             number = number * radix + digit;
1105         } else {
1106             ++exponent;
1107         }
1108         ++p;
1109     }
1110 
1111     // cal pow
1112     int exponentAbs = exponent < 0 ? -exponent : exponent;
1113     double powVal = ((radix == DECIMAL) && (exponentAbs < POWERS_OF_TEN_SIZE)) ?
1114         POWERS_OF_TEN[exponentAbs] : std::pow(radix, exponentAbs);
1115     if (exponent < 0) {
1116         result = number / powVal;
1117     } else {
1118         result = number * powVal;
1119     }
1120     return sign == Sign::NEG ? -result : result;
1121 }
1122 
DoubleToInt(double d,size_t bits)1123 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
1124 {
1125     int32_t ret = 0;
1126     auto u64 = bit_cast<uint64_t>(d);
1127     int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
1128     if (exp < static_cast<int>(bits - 1)) {
1129         // smaller than INT<bits>_MAX, fast conversion
1130         ret = static_cast<int32_t>(d);
1131     } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
1132         // Still has significand bits after mod 2^<bits>
1133         // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
1134         uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
1135                           << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
1136                          (INT64_BITS - bits);
1137         ret = static_cast<int32_t>(value);
1138         if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
1139             ret = -ret;
1140         }
1141     } else {
1142         // No significand bits after mod 2^<bits>, contains NaN and INF
1143         ret = 0;
1144     }
1145     return ret;
1146 }
1147 
DoubleInRangeInt32(double d)1148 int32_t NumberHelper::DoubleInRangeInt32(double d)
1149 {
1150     if (d >= static_cast<double>(INT_MAX)) {
1151         return INT_MAX;
1152     }
1153     if (d <= static_cast<double>(INT_MIN)) {
1154         return INT_MIN;
1155     }
1156     return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
1157 }
1158 
SaturateTruncDoubleToInt32(double d)1159 int32_t NumberHelper::SaturateTruncDoubleToInt32(double d)
1160 {
1161     if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
1162         return 0;
1163     }
1164     return base::NumberHelper::DoubleInRangeInt32(d);
1165 }
1166 
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)1167 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
1168 {
1169     auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
1170     uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
1171     if (strLen == 0) {
1172         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1173     }
1174     CVector<uint8_t> buf;
1175     Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
1176 
1177     auto p = const_cast<uint8_t *>(str.begin());
1178     auto end = str.end();
1179     // 1. skip space and line terminal
1180     if (!NumberHelper::GotoNonspace(&p, end)) {
1181         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1182     }
1183     // 2. get bigint sign
1184     Sign sign = Sign::NONE;
1185     if (*p == '+') {
1186         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1187         sign = Sign::POS;
1188     } else if (*p == '-') {
1189         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1190         sign = Sign::NEG;
1191     }
1192     // 3. bigint not allow Infinity, decimal points, or exponents.
1193     if (isalpha(*p)) {
1194         return JSTaggedValue(NAN_VALUE);
1195     }
1196     // 4. get bigint radix
1197     uint8_t radix = DECIMAL;
1198     if (*p == '0') {
1199         if (++p == end) {
1200             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1201         }
1202         if (*p == 'x' || *p == 'X') {
1203             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1204             if (sign != Sign::NONE) {
1205                 return JSTaggedValue(NAN_VALUE);
1206             }
1207             radix = HEXADECIMAL;
1208         } else if (*p == 'o' || *p == 'O') {
1209             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1210             if (sign != Sign::NONE) {
1211                 return JSTaggedValue(NAN_VALUE);
1212             }
1213             radix = OCTAL;
1214         } else if (*p == 'b' || *p == 'B') {
1215             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1216             if (sign != Sign::NONE) {
1217                 return JSTaggedValue(NAN_VALUE);
1218             }
1219             radix = BINARY;
1220         }
1221     }
1222 
1223     // 5. skip leading '0'
1224     while (*p == '0') {
1225         if (++p == end) {
1226             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1227         }
1228     }
1229     // 6. parse to bigint
1230     CString buffer;
1231     do {
1232         uint8_t c = ToDigit(*p);
1233         if (c < radix) {
1234             buffer += *p;
1235         } else if (NumberHelper::GotoNonspace(&p, end)) {
1236             // illegal character
1237             return JSTaggedValue(NAN_VALUE);
1238         }
1239         // tail of string is space
1240     } while (++p < end);
1241     if (buffer.size() == 0) {
1242         return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
1243     }
1244     if (sign == Sign::NEG) {
1245         return BigIntHelper::SetBigInt(thread, "-" + buffer, radix).GetTaggedValue();
1246     }
1247     return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
1248 }
1249 
GetBase(double d,int digits,int * decimalPoint,char * buf,char * bufTmp,int size)1250 void NumberHelper::GetBase(double d, int digits, int *decimalPoint, char *buf, char *bufTmp, int size)
1251 {
1252     int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
1253     if (result == -1) {
1254         LOG_FULL(FATAL) << "snprintf_s failed";
1255         UNREACHABLE();
1256     }
1257     // mantissa
1258     buf[0] = bufTmp[1];
1259     if (digits > 1) {
1260         if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
1261             LOG_FULL(FATAL) << "memcpy_s failed";
1262             UNREACHABLE();
1263         }
1264     }
1265     buf[digits + 1] = '\0';
1266     // exponent
1267     *decimalPoint = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
1268 }
1269 
GetMinmumDigits(double d,int * decimalPoint,char * buf)1270 int NumberHelper::GetMinmumDigits(double d, int *decimalPoint, char *buf)
1271 {
1272     int digits = 0;
1273     char bufTmp[JS_DTOA_BUF_SIZE] = {0};
1274 
1275     // find the minimum amount of digits
1276     int MinDigits = 1;
1277     int MaxDigits = DOUBLE_MAX_PRECISION;
1278     while (MinDigits < MaxDigits) {
1279         digits = (MinDigits + MaxDigits) / 2; // 2 :  Divide by 2
1280         GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1281         if (strtod(bufTmp, NULL) == d) {
1282             // no need to keep the trailing zeros
1283             while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
1284                 digits--;
1285             }
1286             MaxDigits = digits;
1287         } else {
1288             MinDigits = digits + 1;
1289         }
1290     }
1291     digits = MaxDigits;
1292     GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1293 
1294     return digits;
1295 }
1296 
StringToInt64(const std::string & str,int64_t & value)1297 bool NumberHelper::StringToInt64(const std::string& str, int64_t& value)
1298 {
1299     if (str.empty()) {
1300         return false;
1301     }
1302 
1303     char *end;
1304     errno = 0;
1305     value = std::strtoll(str.c_str(), &end, 0); // Automatic check of the number system
1306 
1307     // If no number is converted
1308     if (end == str.c_str()) {
1309         return false;
1310     }
1311     // If there is a range error (too large or to small)
1312     if (errno == ERANGE && (value == LLONG_MAX || value == LLONG_MIN)) {
1313         return false;
1314     }
1315     // If the character string contains non-digit chaaracters
1316     if (*end != '\0') {
1317         return false;
1318     }
1319 
1320     return true;
1321 }
1322 
XorShift64(uint64_t * pVal)1323 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
1324 {
1325     uint64_t x = *pVal;
1326     x ^= x >> RIGHT12;
1327     x ^= x << LEFT25;
1328     x ^= x >> RIGHT27;
1329     *pVal = x;
1330     return x * GET_MULTIPLY;
1331 }
1332 
InitRandom(JSThread * thread)1333 void RandomGenerator::InitRandom(JSThread *thread)
1334 {
1335     struct timeval tv;
1336     gettimeofday(&tv, NULL);
1337     randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
1338     // the state must be non zero
1339     if (randomState_ == 0) {
1340         randomState_ = 1;
1341     }
1342     thread->SetRandomStatePtr(&randomState_);
1343 }
1344 
NextDouble()1345 double RandomGenerator::NextDouble()
1346 {
1347     uint64_t val = XorShift64(&randomState_);
1348     return ToDouble(val);
1349 }
1350 
ToDouble(uint64_t state)1351 double RandomGenerator::ToDouble(uint64_t state)
1352 {
1353     uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
1354     return base::bit_cast<double>(random) - 1;
1355 }
1356 
Next(int bits)1357 int32_t RandomGenerator::Next(int bits)
1358 {
1359     uint64_t val = XorShift64(&randomState_);
1360     return static_cast<int32_t>(val >> (INT64_BITS - bits));
1361 }
1362 
GenerateIdentityHash()1363 int32_t RandomGenerator::GenerateIdentityHash()
1364 {
1365     return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
1366 }
1367 }  // namespace panda::ecmascript::base
1368