xref: /reactos/sdk/lib/ucrt/convert/fcvt.cpp (revision 04e0dc4a)
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