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