• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023 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 <cstdlib>
17 #include "ets_intrinsics_helpers.h"
18 #include "include/mem/panda_string.h"
19 #include "types/ets_field.h"
20 #include "types/ets_string.h"
21 
22 namespace panda::ets::intrinsics::helpers {
23 
24 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
25 #define RETURN_IF_CONVERSION_END(p, end, result) \
26     if ((p) == (end)) {                          \
27         return (result);                         \
28     }
29 
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)30 double StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
31 {
32     if (IsEmptyString(start, end)) {
33         return NAN_VALUE;
34     }
35 
36     radix = 0;
37     auto p = const_cast<uint8_t *>(start);
38 
39     // 1. skip space and line terminal
40     if (!GotoNonspace(&p, end)) {
41         return 0.0;
42     }
43 
44     // 2. get number sign
45     Sign sign = Sign::NONE;
46     if (*p == '+') {
47         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
48         sign = Sign::POS;
49     } else if (*p == '-') {
50         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
51         sign = Sign::NEG;
52     }
53     bool ignoreTrailing = (flags & flags::IGNORE_TRAILING) != 0;
54 
55     // 3. judge Infinity
56     static const char INF[] = "Infinity";  // NOLINT(modernize-avoid-c-arrays)
57     if (*p == INF[0]) {
58         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
59         for (const char *i = &INF[1]; *i != '\0'; ++i) {
60             if (++p == end || *p != *i) {
61                 return NAN_VALUE;
62             }
63         }
64         ++p;
65         if (!ignoreTrailing && GotoNonspace(&p, end)) {
66             return NAN_VALUE;
67         }
68         return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
69     }
70 
71     // 4. get number radix
72     bool leadingZero = false;
73     bool prefixRadix = false;
74     if (*p == '0' && radix == 0) {
75         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
76         if (*p == 'x' || *p == 'X') {
77             if ((flags & flags::ALLOW_HEX) == 0) {
78                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
79             }
80             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
81             if (sign != Sign::NONE) {
82                 return NAN_VALUE;
83             }
84             prefixRadix = true;
85             radix = HEXADECIMAL;
86         } else if (*p == 'o' || *p == 'O') {
87             if ((flags & flags::ALLOW_OCTAL) == 0) {
88                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
89             }
90             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
91             if (sign != Sign::NONE) {
92                 return NAN_VALUE;
93             }
94             prefixRadix = true;
95             radix = OCTAL;
96         } else if (*p == 'b' || *p == 'B') {
97             if ((flags & flags::ALLOW_BINARY) == 0) {
98                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
99             }
100             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
101             if (sign != Sign::NONE) {
102                 return NAN_VALUE;
103             }
104             prefixRadix = true;
105             radix = BINARY;
106         } else {
107             leadingZero = true;
108         }
109     }
110 
111     if (radix == 0) {
112         radix = DECIMAL;
113     }
114     auto pStart = p;
115     // 5. skip leading '0'
116     while (*p == '0') {
117         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
118         leadingZero = true;
119     }
120     // 6. parse to number
121     uint64_t intNumber = 0;
122     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
123     int digits = 0;
124     int exponent = 0;
125     do {
126         uint8_t c = ToDigit(*p);
127         if (c >= radix) {
128             if (!prefixRadix || ignoreTrailing || (pStart != p && !GotoNonspace(&p, end))) {
129                 break;
130             }
131             // "0b" "0x1.2" "0b1e2" ...
132             return NAN_VALUE;
133         }
134         ++digits;
135         if (intNumber < numberMax) {
136             intNumber = intNumber * radix + c;
137         } else {
138             ++exponent;
139         }
140     } while (++p != end);
141 
142     auto number = static_cast<double>(intNumber);
143     if (sign == Sign::NEG) {
144         if (number == 0) {
145             number = -0.0;
146         } else {
147             number = -number;
148         }
149     }
150 
151     // 7. deal with other radix except DECIMAL
152     if (p == end || radix != DECIMAL) {
153         if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && GotoNonspace(&p, end))) {
154             // no digits there, like "0x", "0xh", or error trailing of "0x3q"
155             return NAN_VALUE;
156         }
157         return number * std::pow(radix, exponent);
158     }
159 
160     // 8. parse '.'
161     if (radix == DECIMAL && *p == '.') {
162         RETURN_IF_CONVERSION_END(++p, end, (digits > 0) ? (number * std::pow(radix, exponent)) : NAN_VALUE);
163         while (ToDigit(*p) < radix) {
164             --exponent;
165             ++digits;
166             if (++p == end) {
167                 break;
168             }
169         }
170     }
171     if (digits == 0 && !leadingZero) {
172         // no digits there, like ".", "sss", or ".e1"
173         return NAN_VALUE;
174     }
175     auto pEnd = p;
176 
177     // 9. parse 'e/E' with '+/-'
178     char exponentSign = '+';
179     int additionalExponent = 0;
180     constexpr int MAX_EXPONENT = INT32_MAX / 2;
181     if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
182         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
183 
184         // 10. parse exponent number
185         if (*p == '+' || *p == '-') {
186             exponentSign = static_cast<char>(*p);
187             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
188         }
189         uint8_t digit;
190         while ((digit = ToDigit(*p)) < radix) {
191             if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
192                 additionalExponent = MAX_EXPONENT;
193             } else {
194                 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
195             }
196             if (++p == end) {
197                 break;
198             }
199         }
200     }
201     exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
202     if (!ignoreTrailing && GotoNonspace(&p, end)) {
203         return NAN_VALUE;
204     }
205 
206     // 10. build StringNumericLiteral string
207     PandaString buffer;
208     if (sign == Sign::NEG) {
209         buffer += "-";
210     }
211     for (uint8_t *i = pStart; i < pEnd; ++i) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
212         if (*i != static_cast<uint8_t>('.')) {
213             buffer += *i;
214         }
215     }
216 
217     // 11. convert none-prefix radix string
218     return Strtod(buffer.c_str(), exponent, radix);
219 }
220 
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)221 double StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
222 {
223     auto p = const_cast<uint8_t *>(start);
224     // 1. skip space and line terminal
225     if (!GotoNonspace(&p, end)) {
226         return NAN_VALUE;
227     }
228 
229     // 2. sign bit
230     bool negative = false;
231     if (*p == '-') {
232         negative = true;
233         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
234     } else if (*p == '+') {
235         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
236     }
237     // 3. 0x or 0X
238     bool stripPrefix = true;
239     // 4. If R != 0, then
240     //     a. If R < 2 or R > 36, return NaN.
241     //     b. If R != 16, let stripPrefix be false.
242     if (radix != 0) {
243         if (radix < MIN_RADIX || radix > MAX_RADIX) {
244             return NAN_VALUE;
245         }
246         if (radix != HEXADECIMAL) {
247             stripPrefix = false;
248         }
249     } else {
250         radix = DECIMAL;
251     }
252     int size = 0;
253     if (stripPrefix) {
254         if (*p == '0') {
255             size++;
256             if (++p != end && (*p == 'x' || *p == 'X')) {
257                 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
258                 radix = HEXADECIMAL;
259             }
260         }
261     }
262 
263     double result = 0;
264     bool isDone = false;
265     do {
266         double part = 0;
267         uint32_t multiplier = 1;
268         for (; p != end; ++p) {
269             // The maximum value to ensure that uint32_t will not overflow
270             const uint32_t maxMultiper = 0xffffffffU / 36U;
271             uint32_t m = multiplier * static_cast<uint32_t>(radix);
272             if (m > maxMultiper) {
273                 break;
274             }
275 
276             int currentBit = static_cast<int>(ToDigit(*p));
277             if (currentBit >= radix) {
278                 isDone = true;
279                 break;
280             }
281             size++;
282             part = part * radix + currentBit;
283             multiplier = m;
284         }
285         result = result * multiplier + part;
286         if (isDone) {
287             break;
288         }
289     } while (p != end);
290 
291     if (size == 0) {
292         return NAN_VALUE;
293     }
294 
295     return negative ? -result : result;
296 }
297 
DoubleToExponential(double number,int digit)298 EtsString *DoubleToExponential(double number, int digit)
299 {
300     PandaStringStream ss;
301     if (digit < 0) {
302         ss << std::setiosflags(std::ios::scientific) << std::setprecision(MAX_PRECISION) << number;
303     } else {
304         ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
305     }
306     PandaString result = ss.str();
307     size_t found = result.find_last_of('e');
308     if (found != PandaString::npos && found < result.size() - 2U && result[found + 2U] == '0') {
309         result.erase(found + 2U, 1);  // 2:offset of e
310     }
311     if (digit < 0) {
312         size_t end = found;
313         while (--found > 0) {
314             if (result[found] != '0') {
315                 break;
316             }
317         }
318         if (result[found] == '.') {
319             found--;
320         }
321         if (found < end - 1) {
322             result.erase(found + 1, end - found - 1);
323         }
324     }
325     return EtsString::CreateFromMUtf8(result.c_str());
326 }
327 
DoubleToFixed(double number,int digit)328 EtsString *DoubleToFixed(double number, int digit)
329 {
330     PandaStringStream ss;
331     ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
332     return EtsString::CreateFromMUtf8(ss.str().c_str());
333 }
334 
DoubleToPrecision(double number,int digit)335 EtsString *DoubleToPrecision(double number, int digit)
336 {
337     if (number == 0.0) {
338         return DoubleToFixed(number, digit - 1);
339     }
340     PandaStringStream ss;
341     double positiveNumber = number > 0 ? number : -number;
342     int logDigit = std::floor(log10(positiveNumber));
343     int radixDigit = digit - logDigit - 1;
344     const int maxExponentDigit = 6;
345     if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= maxExponentDigit)) {
346         return DoubleToFixed(number, std::abs(radixDigit));
347     }
348     return DoubleToExponential(number, digit - 1);
349 }
350 
GetStdDoubleArgument(ObjectHeader * obj)351 double GetStdDoubleArgument(ObjectHeader *obj)
352 {
353     auto *cls = obj->ClassAddr<Class>();
354 
355     // Assume std.core.Double has only one `double` field
356     ASSERT(cls->GetInstanceFields().size() == 1);
357 
358     Field &fieldVal = cls->GetInstanceFields()[0];
359 
360     ASSERT(fieldVal.GetTypeId() == panda_file::Type::TypeId::F64);
361 
362     size_t offset = fieldVal.GetOffset();
363     return obj->GetFieldPrimitive<double>(offset);
364 }
365 
366 }  // namespace panda::ets::intrinsics::helpers
367