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