Coverage Report

Created: 2025-06-24 06:43

/src/hermes/external/dtoa/dtoa.h
Line
Count
Source
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
// dtoa doesn't provide a header file so this simple one was created.
9
10
#ifndef HERMES_DTOA_DTOA_H
11
#define HERMES_DTOA_DTOA_H
12
13
#ifdef __cplusplus
14
extern "C" {
15
#endif
16
17
/// dtoa functions need to allocate memory and that job is handled by the dtoa
18
/// allocator. \c dtoa_alloc is an opaque struct representing the allocator. It
19
/// is created by calling \c dtoa_alloc_init() with a pointer to a memory
20
/// buffer declared with \c DECL_DTOA_ALLOC_MEM(name, size).
21
///
22
/// The allocator metadata itself is placed in that memory buffer and the rest
23
/// of it is used to satisfy memory allocations. If it is not enough, additional
24
/// allocations are made in the regular heap with malloc()/free(). So, the
25
/// larger the buffer declared by \c DECL_DTOA_ALLOC_MEM(), the less probability
26
/// there that a heap allocation will be needed.
27
///
28
/// The allocator is not thread safe, so we must guarantee that it is used only
29
/// by one thread a time. Usually we just create it on the stack (which is very
30
/// fast), but it could live in other places, as long as the single thread
31
/// requirement is satisfied.
32
///
33
/// The allocator metadata itself is less than 128 bytes - the rest is the
34
/// "allocation buffer". The dtoa documentation states that 2304 byte allocation
35
/// buffer is sufficient for most cases except the unusual ones, and a 7400 byte
36
/// allocation buffer is sufficient for all cases.
37
///
38
/// We don't need to avoid heap allocation at all costs, so we have chosen a
39
/// total allocator size of 1200 bytes, which seems to avoid heap allocations
40
/// for "normal" cases.
41
typedef struct dtoa_alloc dtoa_alloc;
42
43
/// The minimal size of the dtoa allocator memory buffer. The metadata is less
44
/// than 128 bytes and the rest is available to satisfy dtoa allocations.
45
#define DTOA_ALLOC_MIN_SIZE 256
46
/// The default size of the dtoa allocator memory buffer, which we use when
47
/// declaring it on the stack. The value attempts to find balance between
48
/// excessive stack consumption and avoiding allocations for "normal" cases.
49
#define DTOA_ALLOC_DEFAULT_SIZE 1200
50
51
/// This macro is used to declare a memory buffer for the dtoa allocator. Most
52
/// of all it ensures that the memory is properly aligned. The variable declared
53
/// by this macro is then passed to \c dtoa_alloc_init().
54
#define DECL_DTOA_ALLOC_MEM(name, bytelen) \
55
  union {                                  \
56
    void *p;                               \
57
    double d;                              \
58
    long long l;                           \
59
    char mem[(bytelen)];                   \
60
  } name
61
62
/// Initialize an allocator using the specified memory buffer, and return a
63
/// pointer to the allocator. Note that this does not stipulate that the
64
/// returned pointer will equal \c mem.
65
dtoa_alloc *dtoa_alloc_init(void *mem, int bytelen);
66
67
/// Destroy the previously initialized allocator. Primarily, this call frees
68
/// any heap allocations in the allocator.
69
void dtoa_alloc_done(dtoa_alloc *dalloc);
70
71
/// Converts double into ascii string.
72
/// \param dd the double to convert.
73
/// \param mode the rounding mode, 0 for default.<ul>
74
///     <li>0 ==> shortest string that yields d when read in
75
///             and rounded to nearest.
76
///     <li>1 ==> like 0, but with Steele & White stopping rule;
77
///             e.g. with IEEE P754 arithmetic , mode 0 gives
78
///             1e23 whereas mode 1 gives 9.999999999999999e22.
79
///     <li>2 ==> max(1,ndigits) significant digits.  This gives a
80
///             return value similar to that of ecvt, except
81
///             that trailing zeros are suppressed.
82
///     <li>3 ==> through ndigits past the decimal point.  This
83
///             gives a return value similar to that from fcvt,
84
///             except that trailing zeros are suppressed, and
85
///             ndigits can be negative.
86
///     <li>4,5 ==> similar to 2 and 3, respectively, but (in
87
///             round-nearest mode) with the tests of mode 0 to
88
///             possibly return a shorter string that rounds to d.
89
///             With IEEE arithmetic and compilation with
90
///             -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
91
///             as modes 2 and 3 when FLT_ROUNDS != 1.
92
///     <li>6-9 ==> Debugging modes similar to mode - 4:  don't try
93
///             fast floating-point estimate (if applicable).
94
///     <li>Values of mode other than 0-9 are treated as mode 0.
95
/// </ul>
96
/// \param ndigits number of digits of precision, 0 for default.
97
/// \param decpt where to store position of the decimal. (n in ES5.1 9.8.1)
98
/// \param sign location to store 1 if negative number, 0 if positive number.
99
/// \param rve location to store pointer to the end of the returned string.
100
/// \return string representation of s in ES5.1 9.8.1
101
char *g_dtoa(
102
    dtoa_alloc *dalloc,
103
    double dd,
104
    int mode,
105
    int ndigits,
106
    int *decpt,
107
    int *sign,
108
    char **rve);
109
110
/// Same as dtoa, but #defines ROUND_BIASED, which enables the mode which is
111
/// used for getting results with a fixed number of digits after the decimal.
112
/// It also modifies a check in dtoa (see the NOTE in dtoa_fixed.c),
113
/// which ensures that 0.5 does not get flushed to 0, but rather rounds up to 1.
114
/// A separate function is necessary because dtoa needs compilation flags
115
/// to change options and provides no runtime means of doing so,
116
/// and modification of the code was needed to ensure correctly biased rounding.
117
char *dtoa_fixedpoint(
118
    dtoa_alloc *dalloc,
119
    double dd,
120
    int mode,
121
    int ndigits,
122
    int *decpt,
123
    int *sign,
124
    char **rve);
125
126
/// Free the result of \c g_dtoa() and \c dtoa_fixedpoint().
127
void g_freedtoa(dtoa_alloc *dalloc, char *);
128
129
char *g_fmt(char *, double);
130
double hermes_g_strtod(const char *s00, char **se);
131
132
#ifdef __cplusplus
133
}
134
#endif
135
136
#ifdef __cplusplus
137
/// A convenience RAII wrapper around a dtoa allocator. The usage should be
138
/// self-explanatory. Declare it on the stack (or as a class member), supplying
139
/// the memory buffer size, and pass it to the dtoa functions.
140
template <int bytelen = DTOA_ALLOC_DEFAULT_SIZE>
141
class DtoaAllocator {
142
 public:
143
  DtoaAllocator(const DtoaAllocator &) = delete;
144
  void operator=(const DtoaAllocator &) = delete;
145
146
6.12k
  DtoaAllocator() {
147
6.12k
    dalloc_ = dtoa_alloc_init(&mem_, bytelen);
148
6.12k
  }
149
6.12k
  ~DtoaAllocator() {
150
6.12k
    dtoa_alloc_done(dalloc_);
151
6.12k
  }
152
153
12.2k
  operator dtoa_alloc *() {
154
12.2k
    return dalloc_;
155
12.2k
  }
156
157
 private:
158
  DECL_DTOA_ALLOC_MEM(mem_, bytelen);
159
  dtoa_alloc *dalloc_;
160
};
161
#endif
162
163
#endif // HERMES_DTOA_DTOA_H