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