]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/c++17/floating_from_chars.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / floating_from_chars.cc
1 // std::from_chars implementation for floating-point types -*- C++ -*-
2
3 // Copyright (C) 2020-2021 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 //
26 // ISO C++ 14882:2017
27 // 23.2.9 Primitive numeric input conversion [utility.from.chars]
28 //
29
30 // Prefer to use std::pmr::string if possible, which requires the cxx11 ABI.
31 #define _GLIBCXX_USE_CXX11_ABI 1
32
33 #include <charconv>
34 #include <string>
35 #include <memory_resource>
36 #include <cfenv>
37 #include <cmath>
38 #include <cstdlib>
39 #include <cstring>
40 #include <cctype>
41 #include <locale.h>
42 #include <bits/functexcept.h>
43 #if _GLIBCXX_HAVE_XLOCALE_H
44 # include <xlocale.h>
45 #endif
46
47 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
48 #ifndef __LONG_DOUBLE_IBM128__
49 #error "floating_from_chars.cc must be compiled with -mabi=ibmlongdouble"
50 #endif
51 // strtold for __ieee128
52 extern "C" __ieee128 __strtoieee128(const char*, char**);
53 #endif
54
55 #if _GLIBCXX_HAVE_USELOCALE
56 namespace std _GLIBCXX_VISIBILITY(default)
57 {
58 _GLIBCXX_BEGIN_NAMESPACE_VERSION
59
60 namespace
61 {
62 // A memory resource with a static buffer that can be used for small
63 // allocations. At most one allocation using the freestore can be done
64 // if the static buffer is insufficient. The callers below only require
65 // a single allocation, so there's no need for anything more complex.
66 struct buffer_resource : pmr::memory_resource
67 {
68 ~buffer_resource() { if (m_ptr) operator delete(m_ptr, m_bytes); }
69
70 void*
71 do_allocate(size_t bytes, size_t alignment [[maybe_unused]]) override
72 {
73 // Allocate from the buffer if it will fit.
74 if (m_bytes < sizeof(m_buf) && (m_bytes + bytes) <= sizeof(m_buf))
75 return m_buf + std::__exchange(m_bytes, m_bytes + bytes);
76
77 __glibcxx_assert(m_ptr == nullptr);
78 __glibcxx_assert(alignment != 1);
79
80 m_ptr = operator new(bytes);
81 m_bytes = bytes;
82 return m_ptr;
83 }
84
85 void
86 do_deallocate(void*, size_t, size_t) noexcept override
87 { /* like pmr::monotonic_buffer_resource, do nothing here */ }
88
89 bool
90 do_is_equal(const pmr::memory_resource& other) const noexcept override
91 { return &other == this; }
92
93 static constexpr int guaranteed_capacity() { return sizeof(m_buf); }
94
95 private:
96 char m_buf[512];
97 size_t m_bytes = 0;
98 void* m_ptr = nullptr;
99 };
100
101 #if _GLIBCXX_USE_CXX11_ABI
102 using buffered_string = std::pmr::string;
103 #else
104 using buffered_string = std::string;
105 #endif
106
107 inline bool valid_fmt(chars_format fmt)
108 {
109 return fmt != chars_format{}
110 && ((fmt & chars_format::general) == fmt
111 || (fmt & chars_format::hex) == fmt);
112 }
113
114 constexpr char hex_digits[] = "abcdefABCDEF0123456789";
115 constexpr auto dec_digits = hex_digits + 12;
116
117 // Find initial portion of [first, last) containing a floating-point number.
118 // The string `digits` is either `dec_digits` or `hex_digits`
119 // and `exp` is 'e' or 'p' or '\0'.
120 const char*
121 find_end_of_float(const char* first, const char* last, const char* digits,
122 char exp)
123 {
124 while (first < last && strchr(digits, *first) != nullptr)
125 ++first;
126 if (first < last && *first == '.')
127 {
128 ++first;
129 while (first < last && strchr(digits, *first))
130 ++first;
131 }
132 if (first < last && exp != 0 && std::tolower((unsigned char)*first) == exp)
133 {
134 ++first;
135 if (first < last && (*first == '-' || *first == '+'))
136 ++first;
137 while (first < last && strchr(dec_digits, *first) != nullptr)
138 ++first;
139 }
140 return first;
141 }
142
143 // Determine the prefix of [first, last) that matches the pattern
144 // corresponding to `fmt`.
145 // Returns a NTBS containing the pattern, using `buf` to allocate
146 // additional storage if needed.
147 // Returns a nullptr if a valid pattern is not present.
148 const char*
149 pattern(const char* const first, const char* last,
150 chars_format& fmt, buffered_string& buf)
151 {
152 // fmt has the value of one of the enumerators of chars_format.
153 __glibcxx_assert(valid_fmt(fmt));
154
155 string_view res;
156
157 if (first == last || *first == '+') [[unlikely]]
158 return nullptr;
159
160 const int neg = (*first == '-');
161
162 if (std::memchr("iInN", (unsigned char)first[neg], 4))
163 {
164 ptrdiff_t len = last - first;
165 if (len < (3 + neg))
166 return nullptr;
167
168 // possible infinity or NaN, let strtod decide
169 if (first[neg] == 'i' || first[neg] == 'I')
170 {
171 // Need at most 9 chars for "-INFINITY", ignore anything after it.
172 len = std::min(len, ptrdiff_t(neg + 8));
173 }
174 else if (len > (neg + 3) && first[neg + 3] == '(')
175 {
176 // Look for end of "NAN(n-char-sequence)"
177 if (void* p = std::memchr(const_cast<char*>(first)+4, ')', len-4))
178 len = static_cast<char*>(p) + 1 - first;
179 #ifndef __cpp_exceptions
180 if (len > buffer_resource::guaranteed_capacity())
181 {
182 // The character sequence is too large for the buffer.
183 // Allocation failure could terminate the process,
184 // so just return an error via the fmt parameter.
185 fmt = chars_format{};
186 return nullptr;
187 }
188 #endif
189 }
190 else // Only need 4 chars for "-NAN"
191 len = neg + 3;
192
193 buf.assign(first, 0, len);
194 // prevent make_result correcting for "0x"
195 fmt = chars_format::general;
196 return buf.c_str();
197 }
198
199 const char* digits;
200 char* ptr;
201
202 // Assign [first,last) to a std::string to get a NTBS that can be used
203 // with strspn, strtod etc.
204 // If the string would be longer than the fixed buffer inside the
205 // buffer_resource type use find_end_of_float to try to reduce how
206 // much memory is needed, to reduce the chance of std::bad_alloc.
207
208 if (fmt == chars_format::hex)
209 {
210 digits = hex_digits;
211
212 if ((last - first + 2) > buffer_resource::guaranteed_capacity())
213 {
214 last = find_end_of_float(first + neg, last, digits, 'p');
215 #ifndef __cpp_exceptions
216 if ((last - first + 2) > buffer_resource::guaranteed_capacity())
217 {
218 // The character sequence is still too large for the buffer.
219 // Allocation failure could terminate the process,
220 // so just return an error via the fmt parameter.
221 fmt = chars_format{};
222 return nullptr;
223 }
224 #endif
225 }
226
227 buf = "-0x" + !neg;
228 buf.append(first + neg, last);
229 ptr = buf.data() + neg + 2;
230 }
231 else
232 {
233 digits = dec_digits;
234
235 if ((last - first) > buffer_resource::guaranteed_capacity())
236 {
237 last = find_end_of_float(first + neg, last, digits,
238 "e"[fmt == chars_format::fixed]);
239 #ifndef __cpp_exceptions
240 if ((last - first) > buffer_resource::guaranteed_capacity())
241 {
242 // The character sequence is still too large for the buffer.
243 // Allocation failure could terminate the process,
244 // so just return an error via the fmt parameter.
245 fmt = chars_format{};
246 return nullptr;
247 }
248 #endif
249 }
250 buf.assign(first, last);
251 ptr = buf.data() + neg;
252 }
253
254 // "A non-empty sequence of decimal digits" or
255 // "A non-empty sequence of hexadecimal digits"
256 size_t len = std::strspn(ptr, digits);
257 // "possibly containing a radix character,"
258 if (ptr[len] == '.')
259 {
260 const size_t len2 = std::strspn(ptr + len + 1, digits);
261 if (len + len2)
262 ptr += len + 1 + len2;
263 else
264 return nullptr;
265 }
266 else if (len == 0) [[unlikely]]
267 return nullptr;
268 else
269 ptr += len;
270
271 if (fmt == chars_format::fixed)
272 {
273 // Truncate the string to stop strtod parsing past this point.
274 *ptr = '\0';
275 }
276 else if (fmt == chars_format::scientific)
277 {
278 // Check for required exponent part which starts with 'e' or 'E'
279 if (*ptr != 'e' && *ptr != 'E')
280 return nullptr;
281 // then an optional plus or minus sign
282 const int sign = (ptr[1] == '-' || ptr[1] == '+');
283 // then a nonempty sequence of decimal digits
284 if (!std::memchr(dec_digits, (unsigned char)ptr[1+sign], 10))
285 return nullptr;
286 }
287 else if (fmt == chars_format::general)
288 {
289 if (*ptr == 'x' || *ptr == 'X')
290 *ptr = '\0';
291 }
292
293 return buf.c_str();
294 }
295
296 // Convert the NTBS `str` to a floating-point value of type `T`.
297 // If `str` cannot be converted, `value` is unchanged and `0` is returned.
298 // Otherwise, let N be the number of characters consumed from `str`.
299 // On success `value` is set to the converted value and N is returned.
300 // If the converted value is out of range, `value` is unchanged and
301 // -N is returned.
302 template<typename T>
303 ptrdiff_t
304 from_chars_impl(const char* str, T& value, errc& ec) noexcept
305 {
306 if (locale_t loc = ::newlocale(LC_ALL_MASK, "C", (locale_t)0)) [[likely]]
307 {
308 locale_t orig = ::uselocale(loc);
309
310 #if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
311 const int rounding = std::fegetround();
312 if (rounding != FE_TONEAREST)
313 std::fesetround(FE_TONEAREST);
314 #endif
315
316 const int save_errno = errno;
317 errno = 0;
318 char* endptr;
319 T tmpval;
320 #if _GLIBCXX_USE_C99_STDLIB
321 if constexpr (is_same_v<T, float>)
322 tmpval = std::strtof(str, &endptr);
323 else if constexpr (is_same_v<T, double>)
324 tmpval = std::strtod(str, &endptr);
325 else if constexpr (is_same_v<T, long double>)
326 tmpval = std::strtold(str, &endptr);
327 # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
328 else if constexpr (is_same_v<T, __ieee128>)
329 tmpval = __strtoieee128(str, &endptr);
330 # endif
331 #else
332 tmpval = std::strtod(str, &endptr);
333 #endif
334 const int conv_errno = std::__exchange(errno, save_errno);
335
336 #if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
337 if (rounding != FE_TONEAREST)
338 std::fesetround(rounding);
339 #endif
340
341 ::uselocale(orig);
342 ::freelocale(loc);
343
344 const ptrdiff_t n = endptr - str;
345 if (conv_errno == ERANGE) [[unlikely]]
346 {
347 if (__builtin_isinf(tmpval)) // overflow
348 ec = errc::result_out_of_range;
349 else // underflow (LWG 3081 wants to set value = tmpval here)
350 ec = errc::result_out_of_range;
351 }
352 else if (n)
353 {
354 value = tmpval;
355 ec = errc();
356 }
357 return n;
358 }
359 else if (errno == ENOMEM)
360 ec = errc::not_enough_memory;
361
362 return 0;
363 }
364
365 inline from_chars_result
366 make_result(const char* str, ptrdiff_t n, chars_format fmt, errc ec) noexcept
367 {
368 from_chars_result result = { str, ec };
369 if (n != 0)
370 {
371 if (fmt == chars_format::hex)
372 n -= 2; // correct for the "0x" inserted into the pattern
373 result.ptr += n;
374 }
375 else if (fmt == chars_format{}) [[unlikely]]
376 {
377 // FIXME: the standard does not allow this result.
378 ec = errc::not_enough_memory;
379 }
380 return result;
381 }
382
383 #if ! _GLIBCXX_USE_CXX11_ABI
384 inline bool
385 reserve_string(std::string& s) noexcept
386 {
387 __try
388 {
389 s.reserve(buffer_resource::guaranteed_capacity());
390 }
391 __catch (const std::bad_alloc&)
392 {
393 return false;
394 }
395 return true;
396 }
397 #endif
398
399 } // namespace
400
401 // FIXME: This should be reimplemented so it doesn't use strtod and newlocale.
402 // That will avoid the need for any memory allocation, meaning that the
403 // non-conforming errc::not_enough_memory result cannot happen.
404
405 from_chars_result
406 from_chars(const char* first, const char* last, float& value,
407 chars_format fmt) noexcept
408 {
409 errc ec = errc::invalid_argument;
410 #if _GLIBCXX_USE_CXX11_ABI
411 buffer_resource mr;
412 pmr::string buf(&mr);
413 #else
414 string buf;
415 if (!reserve_string(buf))
416 return make_result(first, 0, {}, ec);
417 #endif
418 size_t len = 0;
419 __try
420 {
421 if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
422 len = from_chars_impl(pat, value, ec);
423 }
424 __catch (const std::bad_alloc&)
425 {
426 fmt = chars_format{};
427 }
428 return make_result(first, len, fmt, ec);
429 }
430
431 from_chars_result
432 from_chars(const char* first, const char* last, double& value,
433 chars_format fmt) noexcept
434 {
435 errc ec = errc::invalid_argument;
436 #if _GLIBCXX_USE_CXX11_ABI
437 buffer_resource mr;
438 pmr::string buf(&mr);
439 #else
440 string buf;
441 if (!reserve_string(buf))
442 return make_result(first, 0, {}, ec);
443 #endif
444 size_t len = 0;
445 __try
446 {
447 if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
448 len = from_chars_impl(pat, value, ec);
449 }
450 __catch (const std::bad_alloc&)
451 {
452 fmt = chars_format{};
453 }
454 return make_result(first, len, fmt, ec);
455 }
456
457 from_chars_result
458 from_chars(const char* first, const char* last, long double& value,
459 chars_format fmt) noexcept
460 {
461 errc ec = errc::invalid_argument;
462 #if _GLIBCXX_USE_CXX11_ABI
463 buffer_resource mr;
464 pmr::string buf(&mr);
465 #else
466 string buf;
467 if (!reserve_string(buf))
468 return make_result(first, 0, {}, ec);
469 #endif
470 size_t len = 0;
471 __try
472 {
473 if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
474 len = from_chars_impl(pat, value, ec);
475 }
476 __catch (const std::bad_alloc&)
477 {
478 fmt = chars_format{};
479 }
480 return make_result(first, len, fmt, ec);
481 }
482
483 #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
484 // Make std::from_chars for 64-bit long double an alias for the overload
485 // for double.
486 extern "C" from_chars_result
487 _ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last,
488 long double& value,
489 chars_format fmt) noexcept
490 __attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format")));
491 #endif
492
493 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
494 from_chars_result
495 from_chars(const char* first, const char* last, __ieee128& value,
496 chars_format fmt) noexcept
497 {
498 buffer_resource mr;
499 pmr::string buf(&mr);
500 size_t len = 0;
501 errc ec = errc::invalid_argument;
502 __try
503 {
504 if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
505 len = from_chars_impl(pat, value, ec);
506 }
507 __catch (const std::bad_alloc&)
508 {
509 fmt = chars_format{};
510 }
511 return make_result(first, len, fmt, ec);
512 }
513 #endif
514
515 _GLIBCXX_END_NAMESPACE_VERSION
516 } // namespace std
517 #endif // _GLIBCXX_HAVE_USELOCALE