1 //
2 // fcvt.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the _ecvt and _fcvt families of functions.
7 //
8 #include <corecrt_internal.h>
9 #include <corecrt_internal_fltintrn.h>
10 #include <corecrt_internal_ptd_propagation.h>
11 #include <corecrt_internal_securecrt.h>
12 #include <minmax.h>
13 #include <stdlib.h>
14
15
16
17 // Tries to get the pre-thread conversion buffer; returns nullptr on failure.
try_get_ptd_buffer(__crt_cached_ptd_host & ptd)18 static char* __cdecl try_get_ptd_buffer(__crt_cached_ptd_host& ptd)
19 {
20 __acrt_ptd* const raw_ptd = ptd.get_raw_ptd_noexit();
21 if (!raw_ptd)
22 {
23 return nullptr;
24 }
25
26 if (raw_ptd->_cvtbuf)
27 {
28 return raw_ptd->_cvtbuf;
29 }
30
31 raw_ptd->_cvtbuf = _malloc_crt_t(char, _CVTBUFSIZE).detach();
32
33 return raw_ptd->_cvtbuf;
34 }
35
36
37
38 // An internal helper that wraps the call to convert the STRFLT to a string and
39 // updates all of the data that is used by its callers.
internal_to_string(_Out_writes_z_ (buffer_count)char * const buffer,size_t const buffer_count,STRFLT const strflt,int const requested_digits,int * const decimal_point,int * const sign,__crt_cached_ptd_host & ptd)40 static errno_t __cdecl internal_to_string(
41 _Out_writes_z_(buffer_count) char* const buffer,
42 size_t const buffer_count,
43 STRFLT const strflt,
44 int const requested_digits,
45 int* const decimal_point,
46 int* const sign,
47 __crt_cached_ptd_host& ptd
48 ) throw()
49 {
50 // Make sure we don't overflow the buffer. If the user asks for more digits
51 // than the buffer can handle, truncate it to the maximum size allowed in
52 // the buffer. The maximum size is two less than the buffer size because we
53 // use one character for overflow and one for the null terminator.
54 size_t const minimum_buffer_count = static_cast<size_t>((requested_digits > 0 ? requested_digits : 0) + 2);
55
56 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count >= minimum_buffer_count, ERANGE);
57
58 int const capped_digits = min(requested_digits, static_cast<int>(buffer_count - 2));
59
60 errno_t const e = __acrt_fp_strflt_to_string(buffer, buffer_count, capped_digits, strflt, __acrt_has_trailing_digits::trailing, __acrt_rounding_mode::legacy, ptd);
61
62 if (e != 0)
63 {
64 return ptd.get_errno().set(e);
65 }
66
67 *sign = strflt->sign == '-' ? 1 : 0;
68 *decimal_point = strflt->decpt;
69
70 return 0;
71 }
72
73
74
75 // The _fcvt functions, like the _ecvt functions, convert a floating point value
76 // to a narrow character string. The functions prepare the data for the Fortran
77 // F-format with the number of digits following the decimal point specified by
78 // requested_digits. The position of the decimal point is returned indirectly
79 // through *decimal_point. The correct digit for Fortran F-format is rounded.
80 //
81 // These functions update either (a) the user-provided string (_s-suffixed
82 // function) or (b) the per-thread conversion buffer. The _s-suffixed
83 // function returns zero on success, or an error code on failure. The
84 // *decimal_point and *sign values are updated with the results of the
85 // conversion.
_fcvt_s_internal(char * const buffer,size_t const buffer_count,double const value,int const requested_digits,int * const decimal_point,int * const sign,__crt_cached_ptd_host & ptd)86 static errno_t __cdecl _fcvt_s_internal(
87 char* const buffer,
88 size_t const buffer_count,
89 double const value,
90 int const requested_digits,
91 int* const decimal_point,
92 int* const sign,
93 __crt_cached_ptd_host& ptd
94 )
95 {
96 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL);
97 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0, EINVAL);
98 _RESET_STRING(buffer, buffer_count);
99 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, decimal_point != nullptr, EINVAL);
100 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, sign != nullptr, EINVAL);
101
102 char result_string[_CVTBUFSIZE + 1];
103
104 _strflt strflt{};
105 __acrt_fltout(
106 reinterpret_cast<_CRT_DOUBLE const&>(value),
107 _countof(result_string),
108 __acrt_precision_style::fixed,
109 &strflt,
110 result_string,
111 _countof(result_string));
112
113 int const actual_digits = strflt.decpt + requested_digits;
114
115 bool const buffer_insufficiently_large =
116 requested_digits > 0 && strflt.decpt > 0 &&
117 actual_digits < requested_digits;
118
119 int const capped_digits = buffer_insufficiently_large ? INT_MAX : actual_digits;
120
121 return internal_to_string(buffer, buffer_count, &strflt, capped_digits, decimal_point, sign, ptd);
122 }
123
_fcvt_s(char * const buffer,size_t const buffer_count,double const value,int const requested_digits,int * const decimal_point,int * const sign)124 extern "C" errno_t __cdecl _fcvt_s(
125 char* const buffer,
126 size_t const buffer_count,
127 double const value,
128 int const requested_digits,
129 int* const decimal_point,
130 int* const sign
131 )
132 {
133 __crt_cached_ptd_host ptd;
134 return _fcvt_s_internal(buffer, buffer_count, value, requested_digits, decimal_point, sign, ptd);
135 }
136
_fcvt_internal(double const value,int const requested_digits,int * const decimal_point,int * const sign,__crt_cached_ptd_host & ptd)137 static char* __cdecl _fcvt_internal(
138 double const value,
139 int const requested_digits,
140 int* const decimal_point,
141 int* const sign,
142 __crt_cached_ptd_host& ptd
143 )
144 {
145 char* const buffer = try_get_ptd_buffer(ptd);
146 if (!buffer)
147 {
148 return nullptr;
149 }
150
151 char result_string[_CVTBUFSIZE + 1];
152
153 _strflt strflt{};
154 __acrt_fltout(
155 reinterpret_cast<_CRT_DOUBLE const&>(value),
156 _countof(result_string),
157 __acrt_precision_style::fixed,
158 &strflt,
159 result_string,
160 _countof(result_string));
161
162 // Make sure we don't overflow the buffer. If the user asks for more digits
163 // than the buffer can handle, truncate it to the maximum size allowed in
164 // the buffer. The maximum size is two less than the buffer size because we
165 // use one character for overflow and one for the null terminator.
166 int const capped_digits = min(requested_digits, _CVTBUFSIZE - 2 - strflt.decpt);
167
168 errno_t const status = _fcvt_s_internal(buffer, _CVTBUFSIZE, value, capped_digits, decimal_point, sign, ptd);
169 if (status != 0)
170 {
171 return nullptr;
172 }
173
174 return buffer;
175 }
176
_fcvt(double const value,int const requested_digits,int * const decimal_point,int * const sign)177 extern "C" char* __cdecl _fcvt(
178 double const value,
179 int const requested_digits,
180 int* const decimal_point,
181 int* const sign
182 )
183 {
184 __crt_cached_ptd_host ptd;
185 return _fcvt_internal(value, requested_digits, decimal_point, sign, ptd);
186 }
187
188
189 // The _ecvt functions, which convert a floating point value to a string. The
190 // position of the decimal point relative to the beginning of the string is
191 // stored indirectly through the decimal_point argument, where a negative value
192 // means that the decimal point is to the left of the returned digits. If the
193 // sign of the result is negative, the word pointed to by sign is nonzero;
194 // otherwise it is zero. The low order digit is rounded.
195 //
196 // These functions update either (a) the user-provided string (_s-suffixed
197 // function) or (b) the per-thread conversion buffer. The _s-suffixed
198 // function returns zero on success, or an error code on failure. The
199 // *decimal_point and *sign values are updated with the results of the
200 // conversion.
_ecvt_s_internal(char * const buffer,size_t const buffer_count,double const value,int const requested_digits,int * const decimal_point,int * const sign,__crt_cached_ptd_host & ptd)201 static errno_t __cdecl _ecvt_s_internal(
202 char* const buffer,
203 size_t const buffer_count,
204 double const value,
205 int const requested_digits,
206 int* const decimal_point,
207 int* const sign,
208 __crt_cached_ptd_host& ptd
209 )
210 {
211 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer != nullptr, EINVAL);
212 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, buffer_count > 0, EINVAL);
213 _RESET_STRING(buffer, buffer_count);
214 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, decimal_point != nullptr, EINVAL);
215 _UCRT_VALIDATE_RETURN_ERRCODE(ptd, sign != nullptr, EINVAL);
216
217 char result_string[_CVTBUFSIZE + 1];
218
219 _strflt strflt{};
220 __acrt_fltout(
221 reinterpret_cast<_CRT_DOUBLE const&>(value),
222 _countof(result_string),
223 __acrt_precision_style::fixed,
224 &strflt,
225 result_string,
226 _countof(result_string));
227
228 errno_t const e = internal_to_string(buffer, buffer_count, &strflt, requested_digits, decimal_point, sign, ptd);
229
230 // Make sure we don't overflow the buffer. If the user asks for more digits
231 // than the buffer can handle, truncate it to the maximum size allowed in
232 // the buffer. The maximum size is two less than the buffer size because we
233 // use one character for overflow and one for the null terminator.
234 int const capped_digits = min(requested_digits, static_cast<int>(buffer_count - 2));
235
236 // The conversion function occasionally returns an extra char in the buffer:
237 if (capped_digits >= 0 && buffer[capped_digits])
238 {
239 buffer[capped_digits] = '\0';
240 }
241
242 return e;
243 }
244
_ecvt_s(char * const buffer,size_t const buffer_count,double const value,int const requested_digits,int * const decimal_point,int * const sign)245 extern "C" errno_t __cdecl _ecvt_s(
246 char* const buffer,
247 size_t const buffer_count,
248 double const value,
249 int const requested_digits,
250 int* const decimal_point,
251 int* const sign
252 )
253 {
254 __crt_cached_ptd_host ptd;
255 return _ecvt_s_internal(buffer, buffer_count, value, requested_digits, decimal_point, sign, ptd);
256 }
257
_ecvt_internal(double const value,int const requested_digits,int * const decimal_point,int * const sign,__crt_cached_ptd_host & ptd)258 static char* __cdecl _ecvt_internal(
259 double const value,
260 int const requested_digits,
261 int* const decimal_point,
262 int* const sign,
263 __crt_cached_ptd_host& ptd
264 )
265 {
266 char* const buffer = try_get_ptd_buffer(ptd);
267 if (!buffer)
268 {
269 return nullptr;
270 }
271
272 // Make sure we don't overflow the buffer. If the user asks for more digits
273 // than the buffer can handle, truncate it to the maximum size allowed in
274 // the buffer. The maximum size is two less than the buffer size because we
275 // use one character for overflow and one for the null terminator.
276 int const capped_digits = min(requested_digits, _CVTBUFSIZE - 2);
277
278 errno_t const e = _ecvt_s_internal(buffer, _CVTBUFSIZE, value, capped_digits, decimal_point, sign, ptd);
279 if (e != 0)
280 {
281 return nullptr;
282 }
283
284 return buffer;
285 }
286
_ecvt(double const value,int const requested_digits,int * const decimal_point,int * const sign)287 extern "C" char* __cdecl _ecvt(
288 double const value,
289 int const requested_digits,
290 int* const decimal_point,
291 int* const sign
292 )
293 {
294 __crt_cached_ptd_host ptd;
295 return _ecvt_internal(value, requested_digits, decimal_point, sign, ptd);
296 }
297