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