Coverage Report

Created: 2024-02-11 07:11

/src/hermes/include/hermes/Support/Conversions.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_SUPPORT_CONVERSIONS_H
9
#define HERMES_SUPPORT_CONVERSIONS_H
10
11
#include "hermes/Support/OptValue.h"
12
13
#include "llvh/ADT/StringRef.h"
14
#include "llvh/Support/MathExtras.h"
15
16
#include <cstdint>
17
18
namespace hermes {
19
20
/// Convert a double to a 32-bit integer according to ES5.1 section 9.5.
21
/// It can also be used for converting to an unsigned integer, which has the
22
/// same bit pattern.
23
/// NaN and Infinity are always converted to 0. The rest of the numbers are
24
/// converted to a (conceptually) infinite-width integer and the low 32 bits of
25
/// the integer are then returned.
26
/// This is the out-of-line "slow path" which performs a relatively slow
27
/// conversion by manipulating bits in the representation of the double value.
28
int32_t truncateToInt32SlowPath(double d);
29
30
/// Convert a double to a 32-bit integer according to ES5.1 section 9.5.
31
/// It can also be used for converting to an unsigned integer, which has the
32
/// same bit pattern.
33
/// NaN and Infinity are always converted to 0. The rest of the numbers are
34
/// converted to a (conceptually) infinite-width integer and the low 32 bits of
35
/// the integer are then returned.
36
int32_t truncateToInt32(double d) LLVM_NO_SANITIZE("float-cast-overflow");
37
0
inline int32_t truncateToInt32(double d) {
38
  /// ARM64 has different behavior when the double value can't fit into
39
  /// int64_t (results in 2^63-1 instead of -2^63 on x86-64), and 2^63-1 can't
40
  /// be represented precisely in double, so it's converted to 2^63. The result
41
  /// is that a double value 2^63 still goes through the fast path and
42
  /// eventually is casted to int32_t and -1 is returned, which is wrong. The
43
  /// solution is to use smaller width integer (so every value can be
44
  /// represented in double). In constant path, we check the range of
45
  /// [-2^53, 2^53], where 53 is the number of precision bits for double. In
46
  /// non-constant fast path, we do a left shift followed by right shift of 1
47
  /// bit to avoid imprecise conversion between double and int64_t on 2^63 (
48
  /// the top 2 bits "10" becomes "00" after the shifting).
49
  /// On 32bit platform, this non-constant path produces less efficient code,
50
  /// so instead, we use conversion to int32_t directly.
51
  /// In addition, use __builtin_constant_p() to avoid UB caused by constant
52
  /// propagation.
53
54
0
  if constexpr (sizeof(void *) == 8) {
55
    // Use this builtin to avoid undefined behavior caused by constant
56
    // propagation when \p d can't fit into int64_t.
57
0
#if defined(__GNUC__) || defined(__clang__)
58
0
    if (__builtin_constant_p(d)) {
59
0
#endif
60
      // Be aggressive on constant path, use the maximum precision bits
61
      // of double type for range check.
62
0
      if (d >= (int64_t)(-1ULL << 53) && d <= (1LL << 53)) {
63
0
        return (int32_t)(int64_t)d;
64
0
      }
65
0
#if defined(__GNUC__) || defined(__clang__)
66
0
    } else {
67
0
      int64_t fast = (int64_t)((uint64_t)d << 1) >> 1;
68
0
      if (LLVM_LIKELY(fast == d))
69
0
        return (int32_t)fast;
70
0
    }
71
0
#endif
72
0
  } else {
73
0
#if defined(__GNUC__) || defined(__clang__)
74
0
    if (__builtin_constant_p(d)) {
75
0
#endif
76
      // Converted to int32_t directly on 32bit arch for efficiency.
77
      // Many uint32_t values may fall to slow path though.
78
0
      if (d >= (int64_t)(-1ULL << 53) && d <= (1LL << 53)) {
79
0
        return (int32_t)(int64_t)d;
80
0
      }
81
0
#if defined(__GNUC__) || defined(__clang__)
82
0
    } else {
83
0
      int32_t fast = (int32_t)d;
84
0
      if (LLVM_LIKELY(fast == d))
85
0
        return fast;
86
0
    }
87
0
#endif
88
0
  }
89
0
  return truncateToInt32SlowPath(d);
90
0
}
91
92
0
inline uint32_t truncateToUInt32(double d) {
93
0
  return (uint32_t)truncateToInt32(d);
94
0
}
95
96
/// Convert a string in the range defined by [first, last) to an array index
97
/// following ES5.1 15.4:
98
/// "A property name P (in the form of a String value) is an array index if and
99
/// only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to
100
/// 2**32−1."
101
/// IT must meet the requirements of InputIterator and its 'value_type' must be
102
/// an integral type holding a superset of ASCII, typically char, char16_t,
103
/// char32_t or wchar_t.
104
/// \return an "empty" OptValue if the string isn't a valid index, or the
105
///   converted uint32_t value otherwise.
106
template <typename IT>
107
473k
OptValue<uint32_t> toArrayIndex(IT first, IT last) {
108
  /// Empty string is invalid.
109
473k
  if (first == last)
110
9
    return llvh::None;
111
112
  // Leading 0 is special.
113
473k
  if (*first == '0') {
114
14
    ++first;
115
    // Just "0"?
116
14
    if (first == last)
117
14
      return 0;
118
    // Leading 0 is invalid otherwise.
119
0
    return llvh::None;
120
14
  }
121
122
473k
  uint32_t res = 0;
123
1.39M
  do {
124
1.39M
    auto ch = *first;
125
1.39M
    if (ch < '0' || ch > '9')
126
204k
      return llvh::None;
127
1.18M
    uint64_t tmp = (uint64_t)res * 10 + (ch - '0');
128
    // Check for overflow.
129
1.18M
    if (tmp & ((uint64_t)0xFFFFFFFFu << 32))
130
0
      return llvh::None;
131
1.18M
    res = (uint32_t)tmp;
132
1.18M
  } while (++first != last);
133
134
  // 0xFFFFFFFF is not a valid array index.
135
269k
  if (res == 0xFFFFFFFFu)
136
0
    return llvh::None;
137
138
269k
  return res;
139
269k
}
hermes::OptValue<unsigned int> hermes::toArrayIndex<char const*>(char const*, char const*)
Line
Count
Source
107
473k
OptValue<uint32_t> toArrayIndex(IT first, IT last) {
108
  /// Empty string is invalid.
109
473k
  if (first == last)
110
9
    return llvh::None;
111
112
  // Leading 0 is special.
113
473k
  if (*first == '0') {
114
14
    ++first;
115
    // Just "0"?
116
14
    if (first == last)
117
14
      return 0;
118
    // Leading 0 is invalid otherwise.
119
0
    return llvh::None;
120
14
  }
121
122
473k
  uint32_t res = 0;
123
1.39M
  do {
124
1.39M
    auto ch = *first;
125
1.39M
    if (ch < '0' || ch > '9')
126
204k
      return llvh::None;
127
1.18M
    uint64_t tmp = (uint64_t)res * 10 + (ch - '0');
128
    // Check for overflow.
129
1.18M
    if (tmp & ((uint64_t)0xFFFFFFFFu << 32))
130
0
      return llvh::None;
131
1.18M
    res = (uint32_t)tmp;
132
1.18M
  } while (++first != last);
133
134
  // 0xFFFFFFFF is not a valid array index.
135
269k
  if (res == 0xFFFFFFFFu)
136
0
    return llvh::None;
137
138
269k
  return res;
139
269k
}
Unexecuted instantiation: hermes::OptValue<unsigned int> hermes::toArrayIndex<char16_t const*>(char16_t const*, char16_t const*)
140
141
/// A convenient wrapper around 'toArrayIndex(first,last)'.
142
83.6k
inline OptValue<uint32_t> toArrayIndex(llvh::StringRef str) {
143
83.6k
  return toArrayIndex(str.begin(), str.end());
144
83.6k
}
145
146
/// Attempt to convert a double to a valid JavaScript array number.
147
OptValue<uint32_t> doubleToArrayIndex(double d)
148
    LLVM_NO_SANITIZE("float-cast-overflow");
149
2.57M
inline OptValue<uint32_t> doubleToArrayIndex(double d) {
150
2.57M
  uint32_t index = (uint32_t)d;
151
2.57M
  if (index == d && index != 0xFFFFFFFFu)
152
2.57M
    return index;
153
0
  return llvh::None;
154
2.57M
}
155
156
/// Size of buffer that must be passed to numberToString.
157
const size_t NUMBER_TO_STRING_BUF_SIZE = 32;
158
159
/// Convert a double number to string, following ES5.1 9.8.1.
160
/// \param m the number to convert
161
/// \param dest output buffer
162
/// \param destSize size of dest, at least NUMBER_TO_STRING_BUF_SIZE
163
/// \return the length of the generated string (excluding the terminating zero).
164
size_t numberToString(double m, char *dest, size_t destSize);
165
166
/// Takes a letter (a-z or A-Z) and makes it lowercase.
167
template <typename T>
168
2.72M
inline T charLetterToLower(T ch) {
169
2.72M
  return ch | 32;
170
2.72M
}
Unexecuted instantiation: char16_t hermes::charLetterToLower<char16_t>(char16_t)
char hermes::charLetterToLower<char>(char)
Line
Count
Source
168
2.72M
inline T charLetterToLower(T ch) {
169
2.72M
  return ch | 32;
170
2.72M
}
171
172
/// Takes a non-empty string (without the leading "0x" if hex) and parses it
173
/// as radix \p radix, calling \p digitCallback with the value of each digit,
174
/// going from left to right.
175
/// \param AllowNumericSeparator when true, allow '_' as a separator and ignore
176
/// it when parsing.
177
/// \returns true if the string was successfully parsed, false otherwise.
178
template <bool AllowNumericSeparator, class Iterable, typename Callback>
179
2.45M
bool parseIntWithRadixDigits(Iterable str, int radix, Callback digitCallback) {
180
2.45M
  assert(
181
2.45M
      radix >= 2 && radix <= 36 && "Invalid radix passed to parseIntWithRadix");
182
0
  assert(str.begin() != str.end() && "Empty string");
183
5.17M
  for (auto it = str.begin(); it != str.end(); ++it) {
184
2.72M
    auto c = *it;
185
2.72M
    auto cLow = charLetterToLower(c);
186
2.72M
    if ('0' <= c && c <= '9' && c < '0' + radix) {
187
2.72M
      digitCallback(c - '0');
188
2.72M
    } else if ('a' <= cLow && cLow < 'a' + radix - 10) {
189
0
      digitCallback(cLow - 'a' + 0xa);
190
0
    } else if (AllowNumericSeparator && LLVM_UNLIKELY(c == '_')) {
191
      // Ensure the '_' is in a valid location.
192
      // It can only be between two existing digits.
193
0
      if (it == str.begin() || it == str.end() - 1) {
194
0
        return false;
195
0
      }
196
      // Note that the previous character must not be '_' if the current
197
      // character is '_', because we would have returned None.
198
      // So just check if the next character is '_'.
199
0
      char next = *(it + 1);
200
0
      if (next == '_') {
201
0
        return false;
202
0
      }
203
0
    } else {
204
0
      return false;
205
0
    }
206
2.72M
  }
207
2.45M
  return true;
208
2.45M
}
Unexecuted instantiation: bool hermes::parseIntWithRadixDigits<false, hermes::vm::StringView, hermes::parseIntWithRadix<false, hermes::vm::StringView>(hermes::vm::StringView, int)::{lambda(unsigned char)#1}>(hermes::vm::StringView, int, hermes::parseIntWithRadix<false, hermes::vm::StringView>(hermes::vm::StringView, int)::{lambda(unsigned char)#1})
JSLexer.cpp:bool hermes::parseIntWithRadixDigits<true, llvh::ArrayRef<char>, hermes::parser::JSLexer::scanNumber(hermes::parser::JSLexer::GrammarContext)::$_2>(llvh::ArrayRef<char>, int, hermes::parser::JSLexer::scanNumber(hermes::parser::JSLexer::GrammarContext)::$_2)
Line
Count
Source
179
119k
bool parseIntWithRadixDigits(Iterable str, int radix, Callback digitCallback) {
180
119k
  assert(
181
119k
      radix >= 2 && radix <= 36 && "Invalid radix passed to parseIntWithRadix");
182
0
  assert(str.begin() != str.end() && "Empty string");
183
509k
  for (auto it = str.begin(); it != str.end(); ++it) {
184
389k
    auto c = *it;
185
389k
    auto cLow = charLetterToLower(c);
186
389k
    if ('0' <= c && c <= '9' && c < '0' + radix) {
187
389k
      digitCallback(c - '0');
188
389k
    } else if ('a' <= cLow && cLow < 'a' + radix - 10) {
189
0
      digitCallback(cLow - 'a' + 0xa);
190
0
    } else if (AllowNumericSeparator && LLVM_UNLIKELY(c == '_')) {
191
      // Ensure the '_' is in a valid location.
192
      // It can only be between two existing digits.
193
0
      if (it == str.begin() || it == str.end() - 1) {
194
0
        return false;
195
0
      }
196
      // Note that the previous character must not be '_' if the current
197
      // character is '_', because we would have returned None.
198
      // So just check if the next character is '_'.
199
0
      char next = *(it + 1);
200
0
      if (next == '_') {
201
0
        return false;
202
0
      }
203
0
    } else {
204
0
      return false;
205
0
    }
206
389k
  }
207
119k
  return true;
208
119k
}
bool hermes::parseIntWithRadixDigits<true, llvh::ArrayRef<char>, hermes::parseIntWithRadix<true, llvh::ArrayRef<char> >(llvh::ArrayRef<char>, int)::{lambda(unsigned char)#1}>(llvh::ArrayRef<char>, int, hermes::parseIntWithRadix<true, llvh::ArrayRef<char> >(llvh::ArrayRef<char>, int)::{lambda(unsigned char)#1})
Line
Count
Source
179
2.33M
bool parseIntWithRadixDigits(Iterable str, int radix, Callback digitCallback) {
180
2.33M
  assert(
181
2.33M
      radix >= 2 && radix <= 36 && "Invalid radix passed to parseIntWithRadix");
182
0
  assert(str.begin() != str.end() && "Empty string");
183
4.66M
  for (auto it = str.begin(); it != str.end(); ++it) {
184
2.33M
    auto c = *it;
185
2.33M
    auto cLow = charLetterToLower(c);
186
2.33M
    if ('0' <= c && c <= '9' && c < '0' + radix) {
187
2.33M
      digitCallback(c - '0');
188
2.33M
    } else if ('a' <= cLow && cLow < 'a' + radix - 10) {
189
0
      digitCallback(cLow - 'a' + 0xa);
190
0
    } else if (AllowNumericSeparator && LLVM_UNLIKELY(c == '_')) {
191
      // Ensure the '_' is in a valid location.
192
      // It can only be between two existing digits.
193
0
      if (it == str.begin() || it == str.end() - 1) {
194
0
        return false;
195
0
      }
196
      // Note that the previous character must not be '_' if the current
197
      // character is '_', because we would have returned None.
198
      // So just check if the next character is '_'.
199
0
      char next = *(it + 1);
200
0
      if (next == '_') {
201
0
        return false;
202
0
      }
203
0
    } else {
204
0
      return false;
205
0
    }
206
2.33M
  }
207
2.33M
  return true;
208
2.33M
}
209
210
/// Takes a non-empty string (without the leading "0x" if hex) and parses it
211
/// as radix \p radix.
212
/// \param AllowNumericSeparator when true, allow '_' as a separator and ignore
213
///    it when parsing.
214
/// \returns the double that results on success, empty on error.
215
template <bool AllowNumericSeparator, class Iterable>
216
2.33M
OptValue<double> parseIntWithRadix(Iterable str, int radix) {
217
2.33M
  double result = 0;
218
2.33M
  bool success = parseIntWithRadixDigits<AllowNumericSeparator>(
219
2.33M
      str, radix, [&result, radix](uint8_t d) {
220
2.33M
        result *= radix;
221
2.33M
        result += d;
222
2.33M
      });
Unexecuted instantiation: hermes::parseIntWithRadix<false, hermes::vm::StringView>(hermes::vm::StringView, int)::{lambda(unsigned char)#1}::operator()(unsigned char) const
hermes::parseIntWithRadix<true, llvh::ArrayRef<char> >(llvh::ArrayRef<char>, int)::{lambda(unsigned char)#1}::operator()(unsigned char) const
Line
Count
Source
219
2.33M
      str, radix, [&result, radix](uint8_t d) {
220
2.33M
        result *= radix;
221
2.33M
        result += d;
222
2.33M
      });
223
2.33M
  if (!success)
224
0
    return llvh::None;
225
226
  // The largest value that fits in the 53-bit mantissa (2**53).
227
2.33M
  const double MAX_MANTISSA = 9007199254740992.0;
228
2.33M
  if (result >= MAX_MANTISSA && llvh::isPowerOf2_32(radix)) {
229
    // If the result is too high, manually reconstruct the double if
230
    // the radix is 2, 4, 8, 16, 32.
231
    // Go through the digits bit by bit, and manually round when necessary.
232
0
    result = 0;
233
234
    // Keep track of how far along parsing is using this enum.
235
0
    enum Mode {
236
0
      LEADING_ZERO, // Haven't seen a set bit yet.
237
0
      MANTISSA, // Lower bits that allow exact representation.
238
0
      EXP_LOW_BIT, // Lowest bit of the exponent (determine rounding).
239
0
      EXP_LEADING_ZERO, // Zeros in the exponent.
240
0
      EXPONENT, // Seen a set bit in the exponent.
241
0
    };
242
243
0
    size_t remainingMantissa = 53;
244
0
    double expFactor = 0.0;
245
0
    size_t curDigit = 0;
246
247
0
    bool lastMantissaBit = false;
248
0
    bool lowestExponentBit = false;
249
250
0
    Mode curMode = Mode::LEADING_ZERO;
251
0
    auto itr = str.begin();
252
0
    auto e = str.end();
253
0
    for (size_t bitMask = 0;;) {
254
0
      if (bitMask == 0) {
255
        // Only need to do this check every log2(radix) iterations.
256
0
        if (itr == e) {
257
0
          break;
258
0
        }
259
        // We know it fits in 7 bits after the first pass.
260
0
        char c = (char)*itr;
261
0
        if (AllowNumericSeparator && LLVM_UNLIKELY(c == '_')) {
262
0
          ++itr;
263
0
          continue;
264
0
        }
265
0
        auto cLow = charLetterToLower(c);
266
0
        if ('0' <= c && c <= '9') {
267
0
          curDigit = c - '0';
268
0
        } else {
269
          // Must be valid, else we would have returned NaN on first pass.
270
0
          assert('a' <= cLow && cLow < 'a' + radix - 10);
271
0
          curDigit = cLow - 'a' + 0xa;
272
0
        }
273
0
        ++itr;
274
        // Reset bitmask to look at the first bit.
275
0
        bitMask = radix >> 1;
276
0
      }
277
0
      bool curBit = (curDigit & bitMask) != 0;
278
0
      bitMask >>= 1;
279
280
0
      switch (curMode) {
281
0
        case Mode::LEADING_ZERO:
282
          // Go through the string until we hit a nonzero bit.
283
0
          if (curBit) {
284
0
            --remainingMantissa;
285
0
            result = 1;
286
            // No more leading zeros.
287
0
            curMode = Mode::MANTISSA;
288
0
          }
289
0
          break;
290
0
        case Mode::MANTISSA:
291
          // Read into the lower bits of the mantissa (plain binary).
292
0
          result *= 2;
293
0
          result += curBit;
294
0
          --remainingMantissa;
295
0
          if (remainingMantissa == 0) {
296
            // Out of bits, set the last bit and go to the next curMode.
297
0
            lastMantissaBit = curBit;
298
0
            curMode = Mode::EXP_LOW_BIT;
299
0
          }
300
0
          break;
301
0
        case Mode::EXP_LOW_BIT:
302
0
          lowestExponentBit = curBit;
303
0
          expFactor = 2.0;
304
0
          curMode = Mode::EXP_LEADING_ZERO;
305
0
          break;
306
0
        case Mode::EXP_LEADING_ZERO:
307
0
          if (curBit) {
308
0
            curMode = Mode::EXPONENT;
309
0
          }
310
0
          expFactor *= 2.0;
311
0
          break;
312
0
        case Mode::EXPONENT:
313
0
          expFactor *= 2.0;
314
0
          break;
315
0
      }
316
0
    }
317
0
    switch (curMode) {
318
0
      case Mode::LEADING_ZERO:
319
0
      case Mode::MANTISSA:
320
0
      case Mode::EXP_LOW_BIT:
321
        // Nothing to do here, already read those in.
322
0
        break;
323
0
      case Mode::EXP_LEADING_ZERO:
324
        // Rounding up.
325
0
        result += lowestExponentBit && lastMantissaBit;
326
0
        result *= expFactor;
327
0
        break;
328
0
      case Mode::EXPONENT:
329
        // Rounding up.
330
0
        result += lowestExponentBit;
331
0
        result *= expFactor;
332
0
        break;
333
0
    }
334
0
  }
335
2.33M
  return result;
336
2.33M
}
Unexecuted instantiation: hermes::OptValue<double> hermes::parseIntWithRadix<false, hermes::vm::StringView>(hermes::vm::StringView, int)
hermes::OptValue<double> hermes::parseIntWithRadix<true, llvh::ArrayRef<char> >(llvh::ArrayRef<char>, int)
Line
Count
Source
216
2.33M
OptValue<double> parseIntWithRadix(Iterable str, int radix) {
217
2.33M
  double result = 0;
218
2.33M
  bool success = parseIntWithRadixDigits<AllowNumericSeparator>(
219
2.33M
      str, radix, [&result, radix](uint8_t d) {
220
2.33M
        result *= radix;
221
2.33M
        result += d;
222
2.33M
      });
223
2.33M
  if (!success)
224
0
    return llvh::None;
225
226
  // The largest value that fits in the 53-bit mantissa (2**53).
227
2.33M
  const double MAX_MANTISSA = 9007199254740992.0;
228
2.33M
  if (result >= MAX_MANTISSA && llvh::isPowerOf2_32(radix)) {
229
    // If the result is too high, manually reconstruct the double if
230
    // the radix is 2, 4, 8, 16, 32.
231
    // Go through the digits bit by bit, and manually round when necessary.
232
0
    result = 0;
233
234
    // Keep track of how far along parsing is using this enum.
235
0
    enum Mode {
236
0
      LEADING_ZERO, // Haven't seen a set bit yet.
237
0
      MANTISSA, // Lower bits that allow exact representation.
238
0
      EXP_LOW_BIT, // Lowest bit of the exponent (determine rounding).
239
0
      EXP_LEADING_ZERO, // Zeros in the exponent.
240
0
      EXPONENT, // Seen a set bit in the exponent.
241
0
    };
242
243
0
    size_t remainingMantissa = 53;
244
0
    double expFactor = 0.0;
245
0
    size_t curDigit = 0;
246
247
0
    bool lastMantissaBit = false;
248
0
    bool lowestExponentBit = false;
249
250
0
    Mode curMode = Mode::LEADING_ZERO;
251
0
    auto itr = str.begin();
252
0
    auto e = str.end();
253
0
    for (size_t bitMask = 0;;) {
254
0
      if (bitMask == 0) {
255
        // Only need to do this check every log2(radix) iterations.
256
0
        if (itr == e) {
257
0
          break;
258
0
        }
259
        // We know it fits in 7 bits after the first pass.
260
0
        char c = (char)*itr;
261
0
        if (AllowNumericSeparator && LLVM_UNLIKELY(c == '_')) {
262
0
          ++itr;
263
0
          continue;
264
0
        }
265
0
        auto cLow = charLetterToLower(c);
266
0
        if ('0' <= c && c <= '9') {
267
0
          curDigit = c - '0';
268
0
        } else {
269
          // Must be valid, else we would have returned NaN on first pass.
270
0
          assert('a' <= cLow && cLow < 'a' + radix - 10);
271
0
          curDigit = cLow - 'a' + 0xa;
272
0
        }
273
0
        ++itr;
274
        // Reset bitmask to look at the first bit.
275
0
        bitMask = radix >> 1;
276
0
      }
277
0
      bool curBit = (curDigit & bitMask) != 0;
278
0
      bitMask >>= 1;
279
280
0
      switch (curMode) {
281
0
        case Mode::LEADING_ZERO:
282
          // Go through the string until we hit a nonzero bit.
283
0
          if (curBit) {
284
0
            --remainingMantissa;
285
0
            result = 1;
286
            // No more leading zeros.
287
0
            curMode = Mode::MANTISSA;
288
0
          }
289
0
          break;
290
0
        case Mode::MANTISSA:
291
          // Read into the lower bits of the mantissa (plain binary).
292
0
          result *= 2;
293
0
          result += curBit;
294
0
          --remainingMantissa;
295
0
          if (remainingMantissa == 0) {
296
            // Out of bits, set the last bit and go to the next curMode.
297
0
            lastMantissaBit = curBit;
298
0
            curMode = Mode::EXP_LOW_BIT;
299
0
          }
300
0
          break;
301
0
        case Mode::EXP_LOW_BIT:
302
0
          lowestExponentBit = curBit;
303
0
          expFactor = 2.0;
304
0
          curMode = Mode::EXP_LEADING_ZERO;
305
0
          break;
306
0
        case Mode::EXP_LEADING_ZERO:
307
0
          if (curBit) {
308
0
            curMode = Mode::EXPONENT;
309
0
          }
310
0
          expFactor *= 2.0;
311
0
          break;
312
0
        case Mode::EXPONENT:
313
0
          expFactor *= 2.0;
314
0
          break;
315
0
      }
316
0
    }
317
0
    switch (curMode) {
318
0
      case Mode::LEADING_ZERO:
319
0
      case Mode::MANTISSA:
320
0
      case Mode::EXP_LOW_BIT:
321
        // Nothing to do here, already read those in.
322
0
        break;
323
0
      case Mode::EXP_LEADING_ZERO:
324
        // Rounding up.
325
0
        result += lowestExponentBit && lastMantissaBit;
326
0
        result *= expFactor;
327
0
        break;
328
0
      case Mode::EXPONENT:
329
        // Rounding up.
330
0
        result += lowestExponentBit;
331
0
        result *= expFactor;
332
0
        break;
333
0
    }
334
0
  }
335
2.33M
  return result;
336
2.33M
}
337
338
} // namespace hermes
339
340
extern "C" {
341
size_t hermes_numberToString(double m, char *dest, size_t destSize);
342
}
343
344
#endif // HERMES_SUPPORT_CONVERSIONS_H