1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/base/number_helper.h"
17
18 #include <cmath>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iomanip>
22 #include <sstream>
23 #include <sys/time.h>
24
25 #include "ecmascript/base/builtins_base.h"
26 #include "ecmascript/base/string_helper.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/object_factory.h"
29
30 namespace panda::ecmascript::base {
31 enum class Sign { NONE, NEG, POS };
32 thread_local uint64_t RandomGenerator::randomState_ {0};
33 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
34 #define RETURN_IF_CONVERSION_END(p, end, result) \
35 if ((p) == (end)) { \
36 return (result); \
37 }
38
39 constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // NOLINT (modernize-avoid-c-arrays)
40 constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
41
ToDigit(uint8_t c)42 static inline uint8_t ToDigit(uint8_t c)
43 {
44 if (c >= '0' && c <= '9') {
45 return c - '0';
46 }
47 if (c >= 'A' && c <= 'Z') {
48 return c - 'A' + DECIMAL;
49 }
50 if (c >= 'a' && c <= 'z') {
51 return c - 'a' + DECIMAL;
52 }
53 return '$';
54 }
55
GotoNonspace(uint8_t ** ptr,const uint8_t * end)56 bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
57 {
58 while (*ptr < end) {
59 uint16_t c = **ptr;
60 size_t size = 1;
61 if (c > INT8_MAX) {
62 size = 0;
63 uint16_t utf8Bit = INT8_MAX + 1; // equal 0b1000'0000
64 while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
65 ++size;
66 utf8Bit >>= 1UL;
67 }
68 if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1, 0) <= 0) {
69 return true;
70 }
71 }
72 if (!StringHelper::IsNonspace(c)) {
73 return true;
74 }
75 *ptr += size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
76 }
77 return false;
78 }
79
SignedZero(Sign sign)80 static inline double SignedZero(Sign sign)
81 {
82 return sign == Sign::NEG ? -0.0 : 0.0;
83 }
84
IsEmptyString(const uint8_t * start,const uint8_t * end)85 bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
86 {
87 auto p = const_cast<uint8_t *>(start);
88 return !NumberHelper::GotoNonspace(&p, end);
89 }
90
DoubleToString(JSThread * thread,double number,int radix)91 JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
92 {
93 bool negative = false;
94 if (number < 0.0) {
95 negative = true;
96 number = -number;
97 }
98
99 double numberInteger = std::floor(number);
100 double numberFraction = number - numberInteger;
101
102 auto value = bit_cast<uint64_t>(number);
103 value += 1;
104 double delta = HALF * (bit_cast<double>(value) - number);
105 delta = std::max(delta, bit_cast<double>(static_cast<uint64_t>(1))); // 1 : The binary of the smallest double is 1
106 CString result;
107 if (numberFraction != 0 && numberFraction >= delta) {
108 result += ".";
109 result += DecimalsToString(&numberInteger, numberFraction, radix, delta);
110 }
111
112 result = IntegerToString(numberInteger, radix) + result;
113
114 if (negative) {
115 result = "-" + result;
116 }
117
118 return BuiltinsBase::GetTaggedString(thread, result.c_str());
119 }
120
DoubleToExponential(JSThread * thread,double number,int digit)121 JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
122 {
123 CStringStream ss;
124 if (digit < 0) {
125 ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number;
126 } else {
127 ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
128 }
129 CString result = ss.str();
130 size_t found = result.find_last_of('e');
131 if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') {
132 result.erase(found + 2, 1); // 2:offset of e
133 }
134 if (digit < 0) {
135 size_t end = found;
136 while (--found > 0) {
137 if (result[found] != '0') {
138 break;
139 }
140 }
141 if (result[found] == '.') {
142 found--;
143 }
144 if (found < end - 1) {
145 result.erase(found + 1, end - found - 1);
146 }
147 }
148 return BuiltinsBase::GetTaggedString(thread, result.c_str());
149 }
150
DoubleToFixed(JSThread * thread,double number,int digit)151 JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit)
152 {
153 CStringStream ss;
154 ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
155 return BuiltinsBase::GetTaggedString(thread, ss.str().c_str());
156 }
157
DoubleToPrecision(JSThread * thread,double number,int digit)158 JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit)
159 {
160 if (number == 0.0) {
161 return DoubleToFixed(thread, number, digit - 1);
162 }
163 CStringStream ss;
164 double positiveNumber = number > 0 ? number : -number;
165 int logDigit = std::floor(log10(positiveNumber));
166 int radixDigit = digit - logDigit - 1;
167 const int MAX_EXPONENT_DIGIT = 6;
168 if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) {
169 return DoubleToFixed(thread, number, std::abs(radixDigit));
170 }
171 return DoubleToExponential(thread, number, digit - 1);
172 }
173
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)174 JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
175 {
176 auto p = const_cast<uint8_t *>(start);
177 JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
178 // 1. skip space and line terminal
179 if (!NumberHelper::GotoNonspace(&p, end)) {
180 return nanResult;
181 }
182
183 // 2. sign bit
184 bool negative = false;
185 if (*p == '-') {
186 negative = true;
187 RETURN_IF_CONVERSION_END(++p, end, nanResult);
188 } else if (*p == '+') {
189 RETURN_IF_CONVERSION_END(++p, end, nanResult);
190 }
191 // 3. 0x or 0X
192 bool stripPrefix = true;
193 // 4. If R 0, then
194 // a. If R < 2 or R > 36, return NaN.
195 // b. If R 16, let stripPrefix be false.
196 if (radix != 0) {
197 if (radix < MIN_RADIX || radix > MAX_RADIX) {
198 return nanResult;
199 }
200 if (radix != HEXADECIMAL) {
201 stripPrefix = false;
202 }
203 } else {
204 radix = DECIMAL;
205 }
206 int size = 0;
207 if (stripPrefix) {
208 if (*p == '0') {
209 size++;
210 if (++p != end && (*p == 'x' || *p == 'X')) {
211 RETURN_IF_CONVERSION_END(++p, end, nanResult);
212 radix = HEXADECIMAL;
213 }
214 }
215 }
216
217 double result = 0;
218 bool isDone = false;
219 do {
220 double part = 0;
221 uint32_t multiplier = 1;
222 for (; p != end; ++p) {
223 // The maximum value to ensure that uint32_t will not overflow
224 const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
225 uint32_t m = multiplier * static_cast<uint32_t>(radix);
226 if (m > MAX_MULTIPER) {
227 break;
228 }
229
230 int currentBit = static_cast<int>(ToDigit(*p));
231 if (currentBit >= radix) {
232 isDone = true;
233 break;
234 }
235 size++;
236 part = part * radix + currentBit;
237 multiplier = m;
238 }
239 result = result * multiplier + part;
240 if (isDone) {
241 break;
242 }
243 } while (p != end);
244
245 if (size == 0) {
246 return nanResult;
247 }
248
249 if (negative) {
250 result = -result;
251 }
252 return BuiltinsBase::GetTaggedDouble(result);
253 }
254
Carry(char current,int radix)255 char NumberHelper::Carry(char current, int radix)
256 {
257 int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
258 digit = (digit == (radix - 1)) ? 0 : digit + 1;
259 return CHARS[digit];
260 }
261
IntegerToString(double number,int radix)262 CString NumberHelper::IntegerToString(double number, int radix)
263 {
264 ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
265 CString result;
266 while (number / radix > MAX_MANTISSA) {
267 number /= radix;
268 result = CString("0").append(result);
269 }
270 do {
271 double remainder = std::fmod(number, radix);
272 result = CHARS[static_cast<int>(remainder)] + result;
273 number = (number - remainder) / radix;
274 } while (number > 0);
275 return result;
276 }
277
DecimalsToString(double * numberInteger,double fraction,int radix,double delta)278 CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta)
279 {
280 CString result;
281 while (fraction >= delta) {
282 fraction *= radix;
283 delta *= radix;
284 int64_t integer = std::floor(fraction);
285 fraction -= integer;
286 result += CHARS[integer];
287 if (fraction > HALF && fraction + delta > 1) {
288 size_t fractionEnd = result.size() - 1;
289 result[fractionEnd] = Carry(*result.rbegin(), radix);
290 for (; fractionEnd > 0; fractionEnd--) {
291 if (result[fractionEnd] == '0') {
292 result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix);
293 } else {
294 break;
295 }
296 }
297 if (fractionEnd == 0) {
298 (*numberInteger)++;
299 }
300 break;
301 }
302 }
303 // delete 0 in the end
304 size_t found = result.find_last_not_of('0');
305 if (found != CString::npos) {
306 result.erase(found + 1);
307 }
308
309 return result;
310 }
311
IntToString(int number)312 CString NumberHelper::IntToString(int number)
313 {
314 return ToCString(number);
315 }
316
IntToEcmaString(const JSThread * thread,int number)317 JSHandle<EcmaString> NumberHelper::IntToEcmaString(const JSThread *thread, int number)
318 {
319 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
320 return factory->NewFromASCII(ToCString(number));
321 }
322
323 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)324 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
325 {
326 ASSERT(number.IsNumber());
327 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
328 if (number.IsInt()) {
329 return factory->NewFromASCII(IntToString(number.GetInt()));
330 }
331
332 double d = number.GetDouble();
333 if (std::isnan(d)) {
334 return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNanCapitalString());
335 }
336 if (d == 0.0) {
337 return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
338 }
339 if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
340 return factory->NewFromASCII(IntToString(static_cast<int32_t>(d)));
341 }
342
343 std::string result;
344 if (d < 0) {
345 result += "-";
346 d = -d;
347 }
348
349 if (std::isinf(d)) {
350 result += "Infinity";
351 return factory->NewFromASCII(result.c_str());
352 }
353
354 ASSERT(d > 0);
355
356 // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m,
357 // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
358 // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note
359 // that k is the number of digits in the decimal representation of s and that s is not divisible by 10.
360 if (0.1 <= d && d < 1) { // 0.1: 10 ** -1
361 // Fast path. In this case, n==0, just need to calculate k and s.
362 std::string resultFast = "0.";
363 int64_t sFast = 0;
364 int kFast = 1;
365 int64_t power = 1;
366 while (kFast <= DOUBLE_MAX_PRECISION) {
367 power *= 10; // 10: base 10
368 int digitFast = static_cast<int64_t>(d * power) % 10; // 10: base 10
369 ASSERT(0 <= digitFast && digitFast <= 9); // 9: single digit max
370 sFast = sFast * 10 + digitFast; // 10: base 10
371 resultFast += (digitFast + '0');
372 if (sFast / static_cast<double>(power) == d) { // s * (10 ** -k)
373 result += resultFast;
374 return factory->NewFromASCII(result.c_str());
375 }
376 kFast++;
377 }
378 }
379 char buffer[JS_DTOA_BUF_SIZE] = {0};
380 int n = 0;
381 int k = GetMinmumDigits(d, &n, buffer);
382 std::string base = buffer;
383 if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers)
384 base.erase(1, 1);
385 if (k <= n) {
386 // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
387 // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
388 // 0x0030 (DIGIT ZERO).
389 base += std::string(n - k, '0');
390 } else {
391 // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
392 // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
393 // the remaining k−n digits of the decimal representation of s.
394 base.insert(n, 1, '.');
395 }
396 } else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers)
397 // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
398 // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
399 // code units of the k digits of the decimal representation of s.
400 base.erase(1, 1);
401 base = std::string("0.") + std::string(-n, '0') + base;
402 } else {
403 if (k == 1) {
404 // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
405 base.erase(1, 1);
406 }
407 // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
408 // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
409 // the decimal representation of the integer abs(n−1) (with no leading zeroes).
410 base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
411 }
412 result += base;
413 return factory->NewFromASCII(result.c_str());
414 }
415
TruncateDouble(double d)416 double NumberHelper::TruncateDouble(double d)
417 {
418 if (std::isnan(d)) {
419 return 0;
420 }
421 if (!std::isfinite(d)) {
422 return d;
423 }
424 // -0 to +0
425 if (d == 0.0) {
426 return 0;
427 }
428 return (d >= 0) ? std::floor(d) : std::ceil(d);
429 }
430
DoubleToInt64(double d)431 int64_t NumberHelper::DoubleToInt64(double d)
432 {
433 if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
434 return std::numeric_limits<int64_t>::max();
435 }
436 if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
437 return std::numeric_limits<int64_t>::min();
438 }
439 return static_cast<int64_t>(d);
440 }
441
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)442 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
443 {
444 auto p = const_cast<uint8_t *>(start);
445 // 1. skip space and line terminal
446 if (!NumberHelper::GotoNonspace(&p, end)) {
447 return 0.0;
448 }
449
450 // 2. get number sign
451 Sign sign = Sign::NONE;
452 if (*p == '+') {
453 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
454 sign = Sign::POS;
455 } else if (*p == '-') {
456 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
457 sign = Sign::NEG;
458 }
459 bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
460
461 // 3. judge Infinity
462 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
463 if (*p == INF[0]) {
464 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
465 for (const char *i = &INF[1]; *i != '\0'; ++i) {
466 if (++p == end || *p != *i) {
467 return NAN_VALUE;
468 }
469 }
470 ++p;
471 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
472 return NAN_VALUE;
473 }
474 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
475 }
476
477 // 4. get number radix
478 bool leadingZero = false;
479 bool prefixRadix = false;
480 if (*p == '0' && radix == 0) {
481 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
482 if (*p == 'x' || *p == 'X') {
483 if ((flags & ALLOW_HEX) == 0) {
484 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
485 }
486 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
487 if (sign != Sign::NONE) {
488 return NAN_VALUE;
489 }
490 prefixRadix = true;
491 radix = HEXADECIMAL;
492 } else if (*p == 'o' || *p == 'O') {
493 if ((flags & ALLOW_OCTAL) == 0) {
494 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
495 }
496 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
497 if (sign != Sign::NONE) {
498 return NAN_VALUE;
499 }
500 prefixRadix = true;
501 radix = OCTAL;
502 } else if (*p == 'b' || *p == 'B') {
503 if ((flags & ALLOW_BINARY) == 0) {
504 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
505 }
506 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
507 if (sign != Sign::NONE) {
508 return NAN_VALUE;
509 }
510 prefixRadix = true;
511 radix = BINARY;
512 } else {
513 leadingZero = true;
514 }
515 }
516
517 if (radix == 0) {
518 radix = DECIMAL;
519 }
520 auto pStart = p;
521 // 5. skip leading '0'
522 while (*p == '0') {
523 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
524 leadingZero = true;
525 }
526 // 6. parse to number
527 uint64_t intNumber = 0;
528 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
529 int digits = 0;
530 int exponent = 0;
531 do {
532 uint8_t c = ToDigit(*p);
533 if (c >= radix) {
534 if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
535 break;
536 }
537 // "0b" "0x1.2" "0b1e2" ...
538 return NAN_VALUE;
539 }
540 ++digits;
541 if (intNumber < numberMax) {
542 intNumber = intNumber * radix + c;
543 } else {
544 ++exponent;
545 }
546 } while (++p != end);
547
548 auto number = static_cast<double>(intNumber);
549 if (sign == Sign::NEG) {
550 if (number == 0) {
551 number = -0.0;
552 } else {
553 number = -number;
554 }
555 }
556
557 // 7. deal with other radix except DECIMAL
558 if (p == end || radix != DECIMAL) {
559 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
560 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
561 return NAN_VALUE;
562 }
563 return number * std::pow(radix, exponent);
564 }
565
566 // 8. parse '.'
567 if (radix == DECIMAL && *p == '.') {
568 RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
569 (number * std::pow(radix, exponent)) : NAN_VALUE);
570 while (ToDigit(*p) < radix) {
571 --exponent;
572 ++digits;
573 if (++p == end) {
574 break;
575 }
576 }
577 }
578 if (digits == 0 && !leadingZero) {
579 // no digits there, like ".", "sss", or ".e1"
580 return NAN_VALUE;
581 }
582 auto pEnd = p;
583
584 // 9. parse 'e/E' with '+/-'
585 char exponentSign = '+';
586 int additionalExponent = 0;
587 constexpr int MAX_EXPONENT = INT32_MAX / 2;
588 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
589 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
590
591 // 10. parse exponent number
592 if (*p == '+' || *p == '-') {
593 exponentSign = static_cast<char>(*p);
594 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
595 }
596 uint8_t digit;
597 while ((digit = ToDigit(*p)) < radix) {
598 if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
599 additionalExponent = MAX_EXPONENT;
600 } else {
601 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
602 }
603 if (++p == end) {
604 break;
605 }
606 }
607 }
608 exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
609 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
610 return NAN_VALUE;
611 }
612
613 // 10. build StringNumericLiteral string
614 CString buffer;
615 if (sign == Sign::NEG) {
616 buffer += "-";
617 }
618 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
619 if (*i != static_cast<uint8_t>('.')) {
620 buffer += *i;
621 }
622 }
623
624 // 11. convert none-prefix radix string
625 return Strtod(buffer.c_str(), exponent, radix);
626 }
627
Strtod(const char * str,int exponent,uint8_t radix)628 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
629 {
630 ASSERT(str != nullptr);
631 ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
632 auto p = const_cast<char *>(str);
633 Sign sign = Sign::NONE;
634 uint64_t number = 0;
635 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
636 double result = 0.0;
637 if (*p == '-') {
638 sign = Sign::NEG;
639 ++p;
640 }
641 while (*p == '0') {
642 ++p;
643 }
644 while (*p != '\0') {
645 uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
646 if (digit >= radix) {
647 break;
648 }
649 if (number < numberMax) {
650 number = number * radix + digit;
651 } else {
652 ++exponent;
653 }
654 ++p;
655 }
656 if (exponent < 0) {
657 result = number / std::pow(radix, -exponent);
658 } else {
659 result = number * std::pow(radix, exponent);
660 }
661 return sign == Sign::NEG ? -result : result;
662 }
663
DoubleToInt(double d,size_t bits)664 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
665 {
666 int32_t ret = 0;
667 auto u64 = bit_cast<uint64_t>(d);
668 int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
669 if (exp < static_cast<int>(bits - 1)) {
670 // smaller than INT<bits>_MAX, fast conversion
671 ret = static_cast<int32_t>(d);
672 } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
673 // Still has significand bits after mod 2^<bits>
674 // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
675 uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
676 << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
677 (INT64_BITS - bits);
678 ret = static_cast<int32_t>(value);
679 if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
680 ret = -ret;
681 }
682 } else {
683 // No significand bits after mod 2^<bits>, contains NaN and INF
684 ret = 0;
685 }
686 return ret;
687 }
688
DoubleInRangeInt32(double d)689 int32_t NumberHelper::DoubleInRangeInt32(double d)
690 {
691 if (d > INT_MAX) {
692 return INT_MAX;
693 }
694 if (d < INT_MIN) {
695 return INT_MIN;
696 }
697 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
698 }
699
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)700 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
701 {
702 auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
703 uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
704 if (strLen == 0) {
705 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
706 }
707 CVector<uint8_t> buf;
708 Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
709
710 auto p = const_cast<uint8_t *>(str.begin());
711 auto end = str.end();
712 // 1. skip space and line terminal
713 if (!NumberHelper::GotoNonspace(&p, end)) {
714 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
715 }
716 // 2. get bigint sign
717 Sign sign = Sign::NONE;
718 if (*p == '+') {
719 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
720 sign = Sign::POS;
721 } else if (*p == '-') {
722 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
723 sign = Sign::NEG;
724 }
725 // 3. bigint not allow Infinity, decimal points, or exponents.
726 if (isalpha(*p)) {
727 return JSTaggedValue(NAN_VALUE);
728 }
729 // 4. get bigint radix
730 uint8_t radix = DECIMAL;
731 if (*p == '0') {
732 if (++p == end) {
733 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
734 }
735 if (*p == 'x' || *p == 'X') {
736 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
737 if (sign != Sign::NONE) {
738 return JSTaggedValue(NAN_VALUE);
739 }
740 radix = HEXADECIMAL;
741 } else if (*p == 'o' || *p == 'O') {
742 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
743 if (sign != Sign::NONE) {
744 return JSTaggedValue(NAN_VALUE);
745 }
746 radix = OCTAL;
747 } else if (*p == 'b' || *p == 'B') {
748 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
749 if (sign != Sign::NONE) {
750 return JSTaggedValue(NAN_VALUE);
751 }
752 radix = BINARY;
753 }
754 }
755
756 auto pStart = p;
757 // 5. skip leading '0'
758 while (*p == '0') {
759 if (++p == end) {
760 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
761 }
762 }
763 // 6. parse to bigint
764 CString buffer;
765 if (sign == Sign::NEG) {
766 buffer += "-";
767 }
768 do {
769 uint8_t c = ToDigit(*p);
770 if (c >= radix) {
771 if (pStart != p && !NumberHelper::GotoNonspace(&p, end)) {
772 break;
773 }
774 return JSTaggedValue(NAN_VALUE);
775 }
776 buffer += *p;
777 } while (++p != end);
778 if (buffer.size() == 0) {
779 return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
780 }
781 return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
782 }
783
GetBase(double d,int digits,int * decpt,char * buf,char * bufTmp,int size)784 void NumberHelper::GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size)
785 {
786 int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
787 if (result == -1) {
788 LOG_FULL(FATAL) << "snprintf_s failed";
789 UNREACHABLE();
790 }
791 // mantissa
792 buf[0] = bufTmp[1];
793 if (digits > 1) {
794 if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
795 LOG_FULL(FATAL) << "memcpy_s failed";
796 UNREACHABLE();
797 }
798 }
799 buf[digits + 1] = '\0';
800 // exponent
801 *decpt = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
802 }
803
GetMinmumDigits(double d,int * decpt,char * buf)804 int NumberHelper::GetMinmumDigits(double d, int *decpt, char *buf)
805 {
806 int digits = 0;
807 char bufTmp[JS_DTOA_BUF_SIZE] = {0};
808
809 // find the minimum amount of digits
810 int MinDigits = 1;
811 int MaxDigits = DOUBLE_MAX_PRECISION;
812 while (MinDigits < MaxDigits) {
813 digits = (MinDigits + MaxDigits) / 2;
814 GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
815 if (strtod(bufTmp, NULL) == d) {
816 // no need to keep the trailing zeros
817 while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
818 digits--;
819 }
820 MaxDigits = digits;
821 } else {
822 MinDigits = digits + 1;
823 }
824 }
825 digits = MaxDigits;
826 GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
827
828 return digits;
829 }
830
XorShift64(uint64_t * pVal)831 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
832 {
833 uint64_t x = *pVal;
834 x ^= x >> RIGHT12;
835 x ^= x << LEFT25;
836 x ^= x >> RIGHT27;
837 *pVal = x;
838 return x * GET_MULTIPLY;
839 }
840
InitRandom()841 void RandomGenerator::InitRandom()
842 {
843 struct timeval tv;
844 gettimeofday(&tv, NULL);
845 randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
846 // the state must be non zero
847 if (randomState_ == 0) {
848 randomState_ = 1;
849 }
850 }
851
NextDouble()852 double RandomGenerator::NextDouble()
853 {
854 uint64_t val = XorShift64(&randomState_);
855 return ToDouble(val);
856 }
857
ToDouble(uint64_t state)858 double RandomGenerator::ToDouble(uint64_t state)
859 {
860 uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
861 return base::bit_cast<double>(random) - 1;
862 }
863
Next(int bits)864 int32_t RandomGenerator::Next(int bits)
865 {
866 uint64_t val = XorShift64(&randomState_);
867 return static_cast<int32_t>(val >> (INT64_BITS - bits));
868 }
869
GenerateIdentityHash()870 int32_t RandomGenerator::GenerateIdentityHash()
871 {
872 return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
873 }
874 } // namespace panda::ecmascript::base
875