/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 |