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