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
IsDigitalString(const uint8_t * start,const uint8_t * end)834 bool NumberHelper::IsDigitalString(const uint8_t *start, const uint8_t *end)
835 {
836 int len = end - start;
837 for (int i = 0; i < len; i++) {
838 if (*(start + i) < '0' || *(start + i) > '9') {
839 return false;
840 }
841 }
842 return true;
843 }
844
StringToInt(const uint8_t * start,const uint8_t * end)845 int NumberHelper::StringToInt(const uint8_t *start, const uint8_t *end)
846 {
847 int num = *start - '0';
848 for (int i = 1; i < (end - start); i++) {
849 num = 10 * num + (*(start + i) - '0'); // 10 : 10 represents the base of the decimal system
850 }
851 return num;
852 }
853
854 // only for string is ordinary string and using UTF8 encoding
855 // Fast path for short integer and some special value
FastStringToNumber(const uint8_t * start,const uint8_t * end,JSTaggedValue string)856 std::pair<bool, JSTaggedNumber> NumberHelper::FastStringToNumber(const uint8_t *start,
857 const uint8_t *end, JSTaggedValue string)
858 {
859 ASSERT(start < end);
860 EcmaStringAccessor strAccessor(string);
861 bool minus = (start[0] == '-');
862 int pos = (minus ? 1 : 0);
863
864 if (pos == (end - start)) {
865 return {true, JSTaggedNumber(NAN_VALUE)};
866 } else if (*(start + pos) > '9') {
867 // valid number's codes not longer than '9', except 'I' and non-breaking space.
868 if (*(start + pos) != 'I' && *(start + pos) != 0xA0) {
869 return {true, JSTaggedNumber(NAN_VALUE)};
870 }
871 } else if ((end - (start + pos)) <= MAX_ELEMENT_INDEX_LEN && IsDigitalString((start + pos), end)) {
872 int num = StringToInt((start + pos), end);
873 if (minus) {
874 if (num == 0) {
875 return {true, JSTaggedNumber(SignedZero(Sign::NEG))};
876 }
877 num = -num;
878 } else {
879 if ((num != 0) || (end - start == 1)) {
880 strAccessor.TryToSetIntegerHash(num);
881 }
882 }
883 return {true, JSTaggedNumber(num)};
884 }
885
886 return {false, JSTaggedNumber(NAN_VALUE)};
887 }
888
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)889 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
890 {
891 auto p = const_cast<uint8_t *>(start);
892 // 1. skip space and line terminal
893 if (!NumberHelper::GotoNonspace(&p, end)) {
894 return 0.0;
895 }
896
897 // 2. get number sign
898 Sign sign = Sign::NONE;
899 if (*p == '+') {
900 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
901 sign = Sign::POS;
902 } else if (*p == '-') {
903 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
904 sign = Sign::NEG;
905 }
906 bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
907
908 // 3. judge Infinity
909 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
910 if (*p == INF[0]) {
911 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
912 for (const char *i = &INF[1]; *i != '\0'; ++i) {
913 if (++p == end || *p != *i) {
914 return NAN_VALUE;
915 }
916 }
917 ++p;
918 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
919 return NAN_VALUE;
920 }
921 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
922 }
923
924 // 4. get number radix
925 bool leadingZero = false;
926 bool prefixRadix = false;
927 if (*p == '0' && radix == 0) {
928 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
929 if (*p == 'x' || *p == 'X') {
930 if ((flags & ALLOW_HEX) == 0) {
931 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
932 }
933 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
934 if (sign != Sign::NONE) {
935 return NAN_VALUE;
936 }
937 prefixRadix = true;
938 radix = HEXADECIMAL;
939 } else if (*p == 'o' || *p == 'O') {
940 if ((flags & ALLOW_OCTAL) == 0) {
941 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
942 }
943 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
944 if (sign != Sign::NONE) {
945 return NAN_VALUE;
946 }
947 prefixRadix = true;
948 radix = OCTAL;
949 } else if (*p == 'b' || *p == 'B') {
950 if ((flags & ALLOW_BINARY) == 0) {
951 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
952 }
953 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
954 if (sign != Sign::NONE) {
955 return NAN_VALUE;
956 }
957 prefixRadix = true;
958 radix = BINARY;
959 } else {
960 leadingZero = true;
961 }
962 }
963
964 if (radix == 0) {
965 radix = DECIMAL;
966 }
967 auto pStart = p;
968 // 5. skip leading '0'
969 while (*p == '0') {
970 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
971 leadingZero = true;
972 }
973 // 6. parse to number
974 uint64_t intNumber = 0;
975 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
976 int digits = 0;
977 int exponent = 0;
978 do {
979 uint8_t c = ToDigit(*p);
980 if (c >= radix) {
981 if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
982 break;
983 }
984 // "0b" "0x1.2" "0b1e2" ...
985 return NAN_VALUE;
986 }
987 ++digits;
988 if (intNumber < numberMax) {
989 intNumber = intNumber * radix + c;
990 } else {
991 ++exponent;
992 }
993 } while (++p != end);
994
995 auto number = static_cast<double>(intNumber);
996 if (sign == Sign::NEG) {
997 if (number == 0) {
998 number = -0.0;
999 } else {
1000 number = -number;
1001 }
1002 }
1003
1004 // 7. deal with other radix except DECIMAL
1005 if (p == end || radix != DECIMAL) {
1006 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
1007 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
1008 return NAN_VALUE;
1009 }
1010 return number * std::pow(radix, exponent);
1011 }
1012
1013 // 8. parse '.'
1014 exponent = 0;
1015 if (radix == DECIMAL && *p == '.') {
1016 RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
1017 (number * std::pow(radix, exponent)) : NAN_VALUE);
1018 while (ToDigit(*p) < radix) {
1019 --exponent;
1020 ++digits;
1021 if (++p == end) {
1022 break;
1023 }
1024 }
1025 }
1026 if (digits == 0 && !leadingZero) {
1027 // no digits there, like ".", "sss", or ".e1"
1028 return NAN_VALUE;
1029 }
1030 auto pEnd = p;
1031
1032 // 9. parse 'e/E' with '+/-'
1033 char exponentSign = '+';
1034 int additionalExponent = 0;
1035 constexpr int MAX_EXPONENT = INT32_MAX / 2;
1036 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
1037 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1038
1039 // 10. parse exponent number
1040 if (*p == '+' || *p == '-') {
1041 exponentSign = static_cast<char>(*p);
1042 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1043 }
1044 uint8_t digit;
1045 while ((digit = ToDigit(*p)) < radix) {
1046 if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
1047 additionalExponent = MAX_EXPONENT;
1048 } else {
1049 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
1050 }
1051 if (++p == end) {
1052 break;
1053 }
1054 }
1055 }
1056 exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
1057 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
1058 return NAN_VALUE;
1059 }
1060
1061 // 10. build StringNumericLiteral string
1062 CString buffer;
1063 if (sign == Sign::NEG) {
1064 buffer += "-";
1065 }
1066 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1067 if (*i != static_cast<uint8_t>('.')) {
1068 buffer += *i;
1069 }
1070 }
1071
1072 // 11. convert none-prefix radix string
1073 return Strtod(buffer.c_str(), exponent, radix);
1074 }
1075
Strtod(const char * str,int exponent,uint8_t radix)1076 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
1077 {
1078 ASSERT(str != nullptr);
1079 ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
1080 auto p = const_cast<char *>(str);
1081 Sign sign = Sign::NONE;
1082 uint64_t number = 0;
1083 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
1084 double result = 0.0;
1085 if (*p == '-') {
1086 sign = Sign::NEG;
1087 ++p;
1088 }
1089 while (*p == '0') {
1090 ++p;
1091 }
1092 while (*p != '\0') {
1093 uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
1094 if (digit >= radix) {
1095 break;
1096 }
1097 if (number < numberMax) {
1098 number = number * radix + digit;
1099 } else {
1100 ++exponent;
1101 }
1102 ++p;
1103 }
1104
1105 // cal pow
1106 int exponentAbs = exponent < 0 ? -exponent : exponent;
1107 double powVal = ((radix == DECIMAL) && (exponentAbs < POWERS_OF_TEN_SIZE)) ?
1108 POWERS_OF_TEN[exponentAbs] : std::pow(radix, exponentAbs);
1109 if (exponent < 0) {
1110 result = number / powVal;
1111 } else {
1112 result = number * powVal;
1113 }
1114 return sign == Sign::NEG ? -result : result;
1115 }
1116
DoubleToInt(double d,size_t bits)1117 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
1118 {
1119 int32_t ret = 0;
1120 auto u64 = bit_cast<uint64_t>(d);
1121 int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
1122 if (exp < static_cast<int>(bits - 1)) {
1123 // smaller than INT<bits>_MAX, fast conversion
1124 ret = static_cast<int32_t>(d);
1125 } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
1126 // Still has significand bits after mod 2^<bits>
1127 // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
1128 uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
1129 << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
1130 (INT64_BITS - bits);
1131 ret = static_cast<int32_t>(value);
1132 if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
1133 ret = -ret;
1134 }
1135 } else {
1136 // No significand bits after mod 2^<bits>, contains NaN and INF
1137 ret = 0;
1138 }
1139 return ret;
1140 }
1141
DoubleInRangeInt32(double d)1142 int32_t NumberHelper::DoubleInRangeInt32(double d)
1143 {
1144 if (d > INT_MAX) {
1145 return INT_MAX;
1146 }
1147 if (d < INT_MIN) {
1148 return INT_MIN;
1149 }
1150 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
1151 }
1152
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)1153 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
1154 {
1155 auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
1156 uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
1157 if (strLen == 0) {
1158 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1159 }
1160 CVector<uint8_t> buf;
1161 Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
1162
1163 auto p = const_cast<uint8_t *>(str.begin());
1164 auto end = str.end();
1165 // 1. skip space and line terminal
1166 if (!NumberHelper::GotoNonspace(&p, end)) {
1167 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1168 }
1169 // 2. get bigint sign
1170 Sign sign = Sign::NONE;
1171 if (*p == '+') {
1172 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1173 sign = Sign::POS;
1174 } else if (*p == '-') {
1175 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1176 sign = Sign::NEG;
1177 }
1178 // 3. bigint not allow Infinity, decimal points, or exponents.
1179 if (isalpha(*p)) {
1180 return JSTaggedValue(NAN_VALUE);
1181 }
1182 // 4. get bigint radix
1183 uint8_t radix = DECIMAL;
1184 if (*p == '0') {
1185 if (++p == end) {
1186 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1187 }
1188 if (*p == 'x' || *p == 'X') {
1189 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1190 if (sign != Sign::NONE) {
1191 return JSTaggedValue(NAN_VALUE);
1192 }
1193 radix = HEXADECIMAL;
1194 } else if (*p == 'o' || *p == 'O') {
1195 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1196 if (sign != Sign::NONE) {
1197 return JSTaggedValue(NAN_VALUE);
1198 }
1199 radix = OCTAL;
1200 } else if (*p == 'b' || *p == 'B') {
1201 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1202 if (sign != Sign::NONE) {
1203 return JSTaggedValue(NAN_VALUE);
1204 }
1205 radix = BINARY;
1206 }
1207 }
1208
1209 // 5. skip leading '0'
1210 while (*p == '0') {
1211 if (++p == end) {
1212 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1213 }
1214 }
1215 // 6. parse to bigint
1216 CString buffer;
1217 do {
1218 uint8_t c = ToDigit(*p);
1219 if (c < radix) {
1220 buffer += *p;
1221 } else if (NumberHelper::GotoNonspace(&p, end)) {
1222 // illegal character
1223 return JSTaggedValue(NAN_VALUE);
1224 }
1225 // tail of string is space
1226 } while (++p < end);
1227 if (buffer.size() == 0) {
1228 return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
1229 }
1230 if (sign == Sign::NEG) {
1231 return BigIntHelper::SetBigInt(thread, "-" + buffer, radix).GetTaggedValue();
1232 }
1233 return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
1234 }
1235
GetBase(double d,int digits,int * decimalPoint,char * buf,char * bufTmp,int size)1236 void NumberHelper::GetBase(double d, int digits, int *decimalPoint, char *buf, char *bufTmp, int size)
1237 {
1238 int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
1239 if (result == -1) {
1240 LOG_FULL(FATAL) << "snprintf_s failed";
1241 UNREACHABLE();
1242 }
1243 // mantissa
1244 buf[0] = bufTmp[1];
1245 if (digits > 1) {
1246 if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
1247 LOG_FULL(FATAL) << "memcpy_s failed";
1248 UNREACHABLE();
1249 }
1250 }
1251 buf[digits + 1] = '\0';
1252 // exponent
1253 *decimalPoint = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
1254 }
1255
GetMinmumDigits(double d,int * decimalPoint,char * buf)1256 int NumberHelper::GetMinmumDigits(double d, int *decimalPoint, char *buf)
1257 {
1258 int digits = 0;
1259 char bufTmp[JS_DTOA_BUF_SIZE] = {0};
1260
1261 // find the minimum amount of digits
1262 int MinDigits = 1;
1263 int MaxDigits = DOUBLE_MAX_PRECISION;
1264 while (MinDigits < MaxDigits) {
1265 digits = (MinDigits + MaxDigits) / 2; // 2 : Divide by 2
1266 GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1267 if (strtod(bufTmp, NULL) == d) {
1268 // no need to keep the trailing zeros
1269 while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
1270 digits--;
1271 }
1272 MaxDigits = digits;
1273 } else {
1274 MinDigits = digits + 1;
1275 }
1276 }
1277 digits = MaxDigits;
1278 GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1279
1280 return digits;
1281 }
1282
StringToInt64(const std::string & str,int64_t & value)1283 bool NumberHelper::StringToInt64(const std::string& str, int64_t& value)
1284 {
1285 if (str.empty()) {
1286 return false;
1287 }
1288
1289 char *end;
1290 errno = 0;
1291 value = std::strtoll(str.c_str(), &end, 0); // Automatic check of the number system
1292
1293 // If no number is converted
1294 if (end == str.c_str()) {
1295 return false;
1296 }
1297 // If there is a range error (too large or to small)
1298 if (errno == ERANGE && (value == LLONG_MAX || value == LLONG_MIN)) {
1299 return false;
1300 }
1301 // If the character string contains non-digit chaaracters
1302 if (*end != '\0') {
1303 return false;
1304 }
1305
1306 return true;
1307 }
1308
XorShift64(uint64_t * pVal)1309 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
1310 {
1311 uint64_t x = *pVal;
1312 x ^= x >> RIGHT12;
1313 x ^= x << LEFT25;
1314 x ^= x >> RIGHT27;
1315 *pVal = x;
1316 return x * GET_MULTIPLY;
1317 }
1318
InitRandom(JSThread * thread)1319 void RandomGenerator::InitRandom(JSThread *thread)
1320 {
1321 struct timeval tv;
1322 gettimeofday(&tv, NULL);
1323 randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
1324 // the state must be non zero
1325 if (randomState_ == 0) {
1326 randomState_ = 1;
1327 }
1328 thread->SetRandomStatePtr(&randomState_);
1329 }
1330
NextDouble()1331 double RandomGenerator::NextDouble()
1332 {
1333 uint64_t val = XorShift64(&randomState_);
1334 return ToDouble(val);
1335 }
1336
ToDouble(uint64_t state)1337 double RandomGenerator::ToDouble(uint64_t state)
1338 {
1339 uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
1340 return base::bit_cast<double>(random) - 1;
1341 }
1342
Next(int bits)1343 int32_t RandomGenerator::Next(int bits)
1344 {
1345 uint64_t val = XorShift64(&randomState_);
1346 return static_cast<int32_t>(val >> (INT64_BITS - bits));
1347 }
1348
GenerateIdentityHash()1349 int32_t RandomGenerator::GenerateIdentityHash()
1350 {
1351 return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
1352 }
1353 } // namespace panda::ecmascript::base
1354