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 int64_t numberInteger = static_cast<int64_t>(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
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(int64_t number,int radix)262 CString NumberHelper::IntegerToString(int64_t number, int radix)
263 {
264 ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
265 CString result;
266 while (number / radix > static_cast<int64_t>(MAX_MANTISSA)) {
267 number /= radix;
268 result = CString("0").append(result);
269 }
270 do {
271 uint8_t remainder = static_cast<uint8_t>(std::fmod(number, radix));
272 result = CHARS[remainder] + result;
273 number = (number - remainder) / radix;
274 } while (number > 0);
275 return result;
276 }
277
DecimalsToString(int64_t * numberInteger,double fraction,int radix,double delta)278 CString NumberHelper::DecimalsToString(int64_t *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
317 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)318 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
319 {
320 ASSERT(number.IsNumber());
321 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
322 if (number.IsInt()) {
323 return factory->NewFromASCII(IntToString(number.GetInt()));
324 }
325
326 double d = number.GetDouble();
327 if (std::isnan(d)) {
328 return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNanCapitalString());
329 }
330 if (d == 0.0) {
331 return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
332 }
333 if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
334 return factory->NewFromASCII(IntToString(static_cast<int32_t>(d)));
335 }
336
337 std::string result;
338 if (d < 0) {
339 result += "-";
340 d = -d;
341 }
342
343 if (std::isinf(d)) {
344 result += "Infinity";
345 return factory->NewFromASCII(result.c_str());
346 }
347
348 ASSERT(d > 0);
349
350 // 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,
351 // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
352 // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note
353 // that k is the number of digits in the decimal representation of s and that s is not divisible by 10.
354 if (0.1 <= d && d < 1) { // 0.1: 10 ** -1
355 // Fast path. In this case, n==0, just need to calculate k and s.
356 std::string resultFast = "0.";
357 int64_t sFast = 0;
358 int kFast = 1;
359 int64_t power = 1;
360 while (kFast <= DOUBLE_MAX_PRECISION) {
361 power *= 10; // 10: base 10
362 int digitFast = static_cast<int64_t>(d * power) % 10; // 10: base 10
363 ASSERT(0 <= digitFast && digitFast <= 9); // 9: single digit max
364 sFast = sFast * 10 + digitFast; // 10: base 10
365 resultFast += (digitFast + '0');
366 if (sFast / static_cast<double>(power) == d) { // s * (10 ** -k)
367 break;
368 }
369 kFast++;
370 }
371 result += resultFast;
372 return factory->NewFromASCII(result.c_str());
373 }
374 char buffer[JS_DTOA_BUF_SIZE] = {0};
375 int n = 0;
376 int k = GetMinmumDigits(d, &n, buffer);
377 std::string base = buffer;
378 if (n > 0 && n <= 21) { // NOLINT(readability-magic-numbers)
379 base.erase(1, 1);
380 if (k <= n) {
381 // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
382 // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
383 // 0x0030 (DIGIT ZERO).
384 base += std::string(n - k, '0');
385 } else {
386 // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
387 // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
388 // the remaining k−n digits of the decimal representation of s.
389 base.insert(n, 1, '.');
390 }
391 } else if (-6 < n && n <= 0) { // NOLINT(readability-magic-numbers)
392 // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
393 // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
394 // code units of the k digits of the decimal representation of s.
395 base.erase(1, 1);
396 base = std::string("0.") + std::string(-n, '0') + base;
397 } else {
398 if (k == 1) {
399 // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
400 base.erase(1, 1);
401 }
402 // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
403 // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
404 // the decimal representation of the integer abs(n−1) (with no leading zeroes).
405 base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
406 }
407 result += base;
408 return factory->NewFromASCII(result.c_str());
409 }
410
TruncateDouble(double d)411 double NumberHelper::TruncateDouble(double d)
412 {
413 if (std::isnan(d)) {
414 return 0;
415 }
416 if (!std::isfinite(d)) {
417 return d;
418 }
419 // -0 to +0
420 if (d == 0.0) {
421 return 0;
422 }
423 return (d >= 0) ? std::floor(d) : std::ceil(d);
424 }
425
DoubleToInt64(double d)426 int64_t NumberHelper::DoubleToInt64(double d)
427 {
428 if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
429 return std::numeric_limits<int64_t>::max();
430 }
431 if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
432 return std::numeric_limits<int64_t>::min();
433 }
434 return static_cast<int64_t>(d);
435 }
436
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)437 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
438 {
439 auto p = const_cast<uint8_t *>(start);
440 // 1. skip space and line terminal
441 if (!NumberHelper::GotoNonspace(&p, end)) {
442 return 0.0;
443 }
444
445 // 2. get number sign
446 Sign sign = Sign::NONE;
447 if (*p == '+') {
448 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
449 sign = Sign::POS;
450 } else if (*p == '-') {
451 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
452 sign = Sign::NEG;
453 }
454 bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
455
456 // 3. judge Infinity
457 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
458 if (*p == INF[0]) {
459 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
460 for (const char *i = &INF[1]; *i != '\0'; ++i) {
461 if (++p == end || *p != *i) {
462 return NAN_VALUE;
463 }
464 }
465 ++p;
466 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
467 return NAN_VALUE;
468 }
469 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
470 }
471
472 // 4. get number radix
473 bool leadingZero = false;
474 bool prefixRadix = false;
475 if (*p == '0' && radix == 0) {
476 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
477 if (*p == 'x' || *p == 'X') {
478 if ((flags & ALLOW_HEX) == 0) {
479 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
480 }
481 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
482 if (sign != Sign::NONE) {
483 return NAN_VALUE;
484 }
485 prefixRadix = true;
486 radix = HEXADECIMAL;
487 } else if (*p == 'o' || *p == 'O') {
488 if ((flags & ALLOW_OCTAL) == 0) {
489 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
490 }
491 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
492 if (sign != Sign::NONE) {
493 return NAN_VALUE;
494 }
495 prefixRadix = true;
496 radix = OCTAL;
497 } else if (*p == 'b' || *p == 'B') {
498 if ((flags & ALLOW_BINARY) == 0) {
499 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
500 }
501 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
502 if (sign != Sign::NONE) {
503 return NAN_VALUE;
504 }
505 prefixRadix = true;
506 radix = BINARY;
507 } else {
508 leadingZero = true;
509 }
510 }
511
512 if (radix == 0) {
513 radix = DECIMAL;
514 }
515 auto pStart = p;
516 // 5. skip leading '0'
517 while (*p == '0') {
518 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
519 leadingZero = true;
520 }
521 // 6. parse to number
522 uint64_t intNumber = 0;
523 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
524 int digits = 0;
525 int exponent = 0;
526 do {
527 uint8_t c = ToDigit(*p);
528 if (c >= radix) {
529 if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
530 break;
531 }
532 // "0b" "0x1.2" "0b1e2" ...
533 return NAN_VALUE;
534 }
535 ++digits;
536 if (intNumber < numberMax) {
537 intNumber = intNumber * radix + c;
538 } else {
539 ++exponent;
540 }
541 } while (++p != end);
542
543 auto number = static_cast<double>(intNumber);
544 if (sign == Sign::NEG) {
545 if (number == 0) {
546 number = -0.0;
547 } else {
548 number = -number;
549 }
550 }
551
552 // 7. deal with other radix except DECIMAL
553 if (p == end || radix != DECIMAL) {
554 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
555 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
556 return NAN_VALUE;
557 }
558 return number * std::pow(radix, exponent);
559 }
560
561 // 8. parse '.'
562 if (radix == DECIMAL && *p == '.') {
563 RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
564 (number * std::pow(radix, exponent)) : NAN_VALUE);
565 while (ToDigit(*p) < radix) {
566 --exponent;
567 ++digits;
568 if (++p == end) {
569 break;
570 }
571 }
572 }
573 if (digits == 0 && !leadingZero) {
574 // no digits there, like ".", "sss", or ".e1"
575 return NAN_VALUE;
576 }
577 auto pEnd = p;
578
579 // 9. parse 'e/E' with '+/-'
580 char exponentSign = '+';
581 int additionalExponent = 0;
582 constexpr int MAX_EXPONENT = INT32_MAX / 2;
583 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
584 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
585
586 // 10. parse exponent number
587 if (*p == '+' || *p == '-') {
588 exponentSign = static_cast<char>(*p);
589 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
590 }
591 uint8_t digit;
592 while ((digit = ToDigit(*p)) < radix) {
593 if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
594 additionalExponent = MAX_EXPONENT;
595 } else {
596 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
597 }
598 if (++p == end) {
599 break;
600 }
601 }
602 }
603 exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
604 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
605 return NAN_VALUE;
606 }
607
608 // 10. build StringNumericLiteral string
609 CString buffer;
610 if (sign == Sign::NEG) {
611 buffer += "-";
612 }
613 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
614 if (*i != static_cast<uint8_t>('.')) {
615 buffer += *i;
616 }
617 }
618
619 // 11. convert none-prefix radix string
620 return Strtod(buffer.c_str(), exponent, radix);
621 }
622
Strtod(const char * str,int exponent,uint8_t radix)623 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
624 {
625 ASSERT(str != nullptr);
626 ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
627 auto p = const_cast<char *>(str);
628 Sign sign = Sign::NONE;
629 uint64_t number = 0;
630 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
631 double result = 0.0;
632 if (*p == '-') {
633 sign = Sign::NEG;
634 ++p;
635 }
636 while (*p == '0') {
637 ++p;
638 }
639 while (*p != '\0') {
640 uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
641 if (digit >= radix) {
642 break;
643 }
644 if (number < numberMax) {
645 number = number * radix + digit;
646 } else {
647 ++exponent;
648 }
649 ++p;
650 }
651 if (exponent < 0) {
652 result = number / std::pow(radix, -exponent);
653 } else {
654 result = number * std::pow(radix, exponent);
655 }
656 return sign == Sign::NEG ? -result : result;
657 }
658
DoubleToInt(double d,size_t bits)659 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
660 {
661 int32_t ret = 0;
662 auto u64 = bit_cast<uint64_t>(d);
663 int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
664 if (exp < static_cast<int>(bits - 1)) {
665 // smaller than INT<bits>_MAX, fast conversion
666 ret = static_cast<int32_t>(d);
667 } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
668 // Still has significand bits after mod 2^<bits>
669 // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
670 uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
671 << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
672 (INT64_BITS - bits);
673 ret = static_cast<int32_t>(value);
674 if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
675 ret = -ret;
676 }
677 } else {
678 // No significand bits after mod 2^<bits>, contains NaN and INF
679 ret = 0;
680 }
681 return ret;
682 }
683
DoubleInRangeInt32(double d)684 int32_t NumberHelper::DoubleInRangeInt32(double d)
685 {
686 if (d > INT_MAX) {
687 return INT_MAX;
688 }
689 if (d < INT_MIN) {
690 return INT_MIN;
691 }
692 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
693 }
694
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)695 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
696 {
697 auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
698 uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
699 if (strLen == 0) {
700 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
701 }
702 [[maybe_unused]] CVector<uint8_t> buf;
703 Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
704
705 auto p = const_cast<uint8_t *>(str.begin());
706 auto end = str.end();
707 // 1. skip space and line terminal
708 if (!NumberHelper::GotoNonspace(&p, end)) {
709 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
710 }
711 // 2. get bigint sign
712 Sign sign = Sign::NONE;
713 if (*p == '+') {
714 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
715 sign = Sign::POS;
716 } else if (*p == '-') {
717 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
718 sign = Sign::NEG;
719 }
720 // 3. bigint not allow Infinity, decimal points, or exponents.
721 if (isalpha(*p)) {
722 return JSTaggedValue(NAN_VALUE);
723 }
724 // 4. get bigint radix
725 uint8_t radix = DECIMAL;
726 if (*p == '0') {
727 if (++p == end) {
728 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
729 }
730 if (*p == 'x' || *p == 'X') {
731 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
732 if (sign != Sign::NONE) {
733 return JSTaggedValue(NAN_VALUE);
734 }
735 radix = HEXADECIMAL;
736 } else if (*p == 'o' || *p == 'O') {
737 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
738 if (sign != Sign::NONE) {
739 return JSTaggedValue(NAN_VALUE);
740 }
741 radix = OCTAL;
742 } else if (*p == 'b' || *p == 'B') {
743 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
744 if (sign != Sign::NONE) {
745 return JSTaggedValue(NAN_VALUE);
746 }
747 radix = BINARY;
748 }
749 }
750
751 auto pStart = p;
752 // 5. skip leading '0'
753 while (*p == '0') {
754 if (++p == end) {
755 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
756 }
757 }
758 // 6. parse to bigint
759 CString buffer;
760 if (sign == Sign::NEG) {
761 buffer += "-";
762 }
763 do {
764 uint8_t c = ToDigit(*p);
765 if (c >= radix) {
766 if (pStart != p && !NumberHelper::GotoNonspace(&p, end)) {
767 break;
768 }
769 return JSTaggedValue(NAN_VALUE);
770 }
771 buffer += *p;
772 } while (++p != end);
773 return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
774 }
775
GetBase(double d,int digits,int * decpt,char * buf,char * bufTmp,int size)776 void NumberHelper::GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size)
777 {
778 int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
779 if (result == -1) {
780 LOG_FULL(FATAL) << "snprintf_s failed";
781 UNREACHABLE();
782 }
783 // mantissa
784 buf[0] = bufTmp[1];
785 if (digits > 1) {
786 if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
787 LOG_FULL(FATAL) << "memcpy_s failed";
788 UNREACHABLE();
789 }
790 }
791 buf[digits + 1] = '\0';
792 // exponent
793 *decpt = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
794 }
795
GetMinmumDigits(double d,int * decpt,char * buf)796 int NumberHelper::GetMinmumDigits(double d, int *decpt, char *buf)
797 {
798 int digits = 0;
799 char bufTmp[JS_DTOA_BUF_SIZE] = {0};
800
801 // find the minimum amount of digits
802 int MinDigits = 1;
803 int MaxDigits = DOUBLE_MAX_PRECISION;
804 while (MinDigits < MaxDigits) {
805 digits = (MinDigits + MaxDigits) / 2;
806 GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
807 if (strtod(bufTmp, NULL) == d) {
808 // no need to keep the trailing zeros
809 while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
810 digits--;
811 }
812 MaxDigits = digits;
813 } else {
814 MinDigits = digits + 1;
815 }
816 }
817 digits = MaxDigits;
818 GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
819
820 return digits;
821 }
GetRandomState()822 uint64_t& RandomGenerator::GetRandomState()
823 {
824 return randomState;
825 }
XorShift64(uint64_t * pVal)826 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
827 {
828 uint64_t x = *pVal;
829 x ^= x >> RIGHT12;
830 x ^= x << LEFT25;
831 x ^= x >> RIGHT27;
832 *pVal = x;
833 return x * GET_MULTIPLY;
834 }
InitRandom()835 void RandomGenerator::InitRandom()
836 {
837 struct timeval tv;
838 gettimeofday(&tv, NULL);
839 randomState = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
840 // the state must be non zero
841 if (randomState == 0)
842 randomState = 1;
843 }
844 } // namespace panda::ecmascript::base
845