diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index ee5e0336f2c..e3926e1c9c2 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -3846,7 +3846,7 @@ changequote([,])dnl fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:28:0 +libtool_VERSION=6:29:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index 8940e0c7acd..8ae3e0fc4bf 100644 --- a/libstdc++-v3/config.h.in +++ b/libstdc++-v3/config.h.in @@ -526,6 +526,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to 1 if you have the `uselocale' function. */ +#undef HAVE_USELOCALE + /* Defined if usleep exists. */ #undef HAVE_USLEEP @@ -556,6 +559,9 @@ /* Define if writev is available in . */ #undef HAVE_WRITEV +/* Define to 1 if you have the header file. */ +#undef HAVE_XLOCALE_H + /* Define to 1 if you have the `_acosf' function. */ #undef HAVE__ACOSF diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index edf4485e607..17aff5d907b 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2299,6 +2299,13 @@ GLIBCXX_3.4.28 { } GLIBCXX_3.4.27; +GLIBCXX_3.4.29 { + + # std::from_chars + _ZSt10from_charsPKcS0_R[def]St12chars_format; + +} GLIBCXX_3.4.28; + # Symbols in the support library (libsupc++) have their own tag. CXXABI_1.3 { diff --git a/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver b/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver index 5ef4a6cb6e1..b4f3af0f9d9 100644 --- a/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver +++ b/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver @@ -40,6 +40,10 @@ GLIBCXX_LDBL_3.4.21 { __gnu_cxx_ldbl1287num_getI[cw]*16_M_extract_floatB5cxx11*; } GLIBCXX_LDBL_3.4.10; +GLIBCXX_LDBL_3.4.29 { + _ZSt10from_charsPKcS0_RgSt12chars_format; +} GLIBCXX_LDBL_3.4.21; + CXXABI_LDBL_1.3 { _ZT[IS]g; _ZT[IS]Pg; diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index dd54bd406a9..8d16bf3ffd6 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -22661,6 +22661,19 @@ fi done +for ac_header in xlocale.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "xlocale.h" "ac_cv_header_xlocale_h" "$ac_includes_default" +if test "x$ac_cv_header_xlocale_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_XLOCALE_H 1 +_ACEOF + +fi + +done + + # Only do link tests if native. Else, hardcode. if $GLIBCXX_IS_NATIVE; then @@ -28986,6 +28999,19 @@ if test "x$ac_cv_func_sockatmark" = xyes; then : #define HAVE_SOCKATMARK 1 _ACEOF +fi +done + + + # Non-standard functions used by C++17 std::from_chars + for ac_func in uselocale +do : + ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale" +if test "x$ac_cv_func_uselocale" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_USELOCALE 1 +_ACEOF + fi done @@ -41997,6 +42023,9 @@ _ACEOF fi + + $as_echo "#define HAVE_USELOCALE 1" >>confdefs.h + ;; *-darwin*) @@ -47770,6 +47799,18 @@ done CXXFLAGS="$ac_save_CXXFLAGS" + + for ac_func in uselocale +do : + ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale" +if test "x$ac_cv_func_uselocale" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_USELOCALE 1 +_ACEOF + +fi +done + ;; *djgpp) @@ -48041,6 +48082,17 @@ if test "x$ac_cv_func_sockatmark" = xyes; then : #define HAVE_SOCKATMARK 1 _ACEOF +fi +done + + for ac_func in uselocale +do : + ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale" +if test "x$ac_cv_func_uselocale" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_USELOCALE 1 +_ACEOF + fi done @@ -54682,6 +54734,17 @@ _ACEOF fi done + for ac_func in uselocale +do : + ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale" +if test "x$ac_cv_func_uselocale" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_USELOCALE 1 +_ACEOF + +fi +done + @@ -75231,7 +75294,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;} fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:28:0 +libtool_VERSION=6:29:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac index ffd0079613f..cbfdf4c6bad 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -256,6 +256,8 @@ AC_CHECK_HEADERS([linux/random.h], [], [], #endif ]]) +AC_CHECK_HEADERS([xlocale.h]) + # Only do link tests if native. Else, hardcode. if $GLIBCXX_IS_NATIVE; then @@ -282,6 +284,9 @@ if $GLIBCXX_IS_NATIVE; then # For Networking TS. AC_CHECK_FUNCS(sockatmark) + # Non-standard functions used by C++17 std::from_chars + AC_CHECK_FUNCS(uselocale) + # For iconv support. AM_ICONV diff --git a/libstdc++-v3/crossconfig.m4 b/libstdc++-v3/crossconfig.m4 index 313f84d05f4..9f2589b739e 100644 --- a/libstdc++-v3/crossconfig.m4 +++ b/libstdc++-v3/crossconfig.m4 @@ -63,6 +63,8 @@ case "${host}" in # We don't yet support AIX's TLS ABI. #GCC_CHECK_TLS AM_ICONV + + AC_DEFINE(HAVE_USELOCALE) ;; *-darwin*) @@ -73,6 +75,8 @@ case "${host}" in # Don't call GLIBCXX_CHECK_LINKER_FEATURES, Darwin doesn't have a GNU ld GLIBCXX_CHECK_MATH_SUPPORT GLIBCXX_CHECK_STDLIB_SUPPORT + + AC_CHECK_FUNCS(uselocale) ;; *djgpp) @@ -129,6 +133,7 @@ case "${host}" in AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc) AC_CHECK_FUNCS(timespec_get) AC_CHECK_FUNCS(sockatmark) + AC_CHECK_FUNCS(uselocale) ;; *-fuchsia*) @@ -190,6 +195,7 @@ case "${host}" in AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc) AC_CHECK_FUNCS(timespec_get) AC_CHECK_FUNCS(sockatmark) + AC_CHECK_FUNCS(uselocale) AM_ICONV ;; *-mingw32*) diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv index cc7dd0e3758..be668c1939e 100644 --- a/libstdc++-v3/include/std/charconv +++ b/libstdc++-v3/include/std/charconv @@ -688,6 +688,20 @@ namespace __detail operator^=(chars_format& __lhs, chars_format __rhs) noexcept { return __lhs = __lhs ^ __rhs; } +#if _GLIBCXX_HAVE_USELOCALE + from_chars_result + from_chars(const char* __first, const char* __last, float& __value, + chars_format __fmt = chars_format::general); + + from_chars_result + from_chars(const char* __first, const char* __last, double& __value, + chars_format __fmt = chars_format::general); + + from_chars_result + from_chars(const char* __first, const char* __last, long double& __value, + chars_format __fmt = chars_format::general); +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // C++14 diff --git a/libstdc++-v3/src/c++17/Makefile.am b/libstdc++-v3/src/c++17/Makefile.am index 85e31fbd91f..642efb976ac 100644 --- a/libstdc++-v3/src/c++17/Makefile.am +++ b/libstdc++-v3/src/c++17/Makefile.am @@ -50,6 +50,7 @@ inst_sources = endif sources = \ + floating_from_chars.cc \ fs_dir.cc \ fs_ops.cc \ fs_path.cc \ diff --git a/libstdc++-v3/src/c++17/Makefile.in b/libstdc++-v3/src/c++17/Makefile.in index de605a3f6a6..ce08eb3ff11 100644 --- a/libstdc++-v3/src/c++17/Makefile.in +++ b/libstdc++-v3/src/c++17/Makefile.in @@ -124,8 +124,8 @@ LTLIBRARIES = $(noinst_LTLIBRARIES) libc__17convenience_la_LIBADD = @ENABLE_DUAL_ABI_TRUE@am__objects_1 = cow-fs_dir.lo cow-fs_ops.lo \ @ENABLE_DUAL_ABI_TRUE@ cow-fs_path.lo -am__objects_2 = fs_dir.lo fs_ops.lo fs_path.lo memory_resource.lo \ - $(am__objects_1) +am__objects_2 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \ + memory_resource.lo $(am__objects_1) @ENABLE_DUAL_ABI_TRUE@am__objects_3 = cow-string-inst.lo @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_4 = ostream-inst.lo \ @ENABLE_EXTERN_TEMPLATE_TRUE@ string-inst.lo $(am__objects_3) @@ -435,6 +435,7 @@ headers = @ENABLE_EXTERN_TEMPLATE_TRUE@ $(extra_string_inst_sources) sources = \ + floating_from_chars.cc \ fs_dir.cc \ fs_ops.cc \ fs_path.cc \ diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc new file mode 100644 index 00000000000..45de2be283d --- /dev/null +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -0,0 +1,422 @@ +// std::from_chars implementation for floating-point types -*- C++ -*- + +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +// +// ISO C++ 14882:2017 +// 23.2.9 Primitive numeric input conversion [utility.from.chars] +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if _GLIBCXX_HAVE_XLOCALE_H +# include +#endif + +#if _GLIBCXX_HAVE_USELOCALE +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +namespace +{ + // A memory resource with a static buffer that can be used for small + // allocations. At most one allocation using the freestore can be done + // if the static buffer is insufficient. The callers below only require + // a single allocation, so there's no need for anything more complex. + struct buffer_resource : pmr::memory_resource + { + ~buffer_resource() { if (m_ptr) operator delete(m_ptr, m_bytes); } + + void* + do_allocate(size_t bytes, size_t alignment [[maybe_unused]]) override + { + // Allocate from the buffer if it will fit. + if (m_bytes < sizeof(m_buf) && (m_bytes + bytes) <= sizeof(m_buf)) + return m_buf + std::__exchange(m_bytes, m_bytes + bytes); + + __glibcxx_assert(m_ptr == nullptr); + __glibcxx_assert(alignment != 1); + + m_ptr = operator new(bytes); + m_bytes = bytes; + return m_ptr; + } + + void + do_deallocate(void*, size_t, size_t) noexcept override + { /* like pmr::monotonic_buffer_resource, do nothing here */ } + + bool + do_is_equal(const pmr::memory_resource& other) const noexcept override + { return &other == this; } + + static constexpr int guaranteed_capacity() { return sizeof(m_buf); } + + private: + char m_buf[512]; + size_t m_bytes = 0; + void* m_ptr = nullptr; + }; + + inline bool valid_fmt(chars_format fmt) + { + return fmt != chars_format{} + && ((fmt & chars_format::general) == fmt + || (fmt & chars_format::hex) == fmt); + } + + constexpr char hex_digits[] = "abcdefABCDEF0123456789"; + constexpr auto dec_digits = hex_digits + 12; + + // Find initial portion of [first, last) containing a floating-point number. + // The string `digits` is either `dec_digits` or `hex_digits` + // and `exp` is 'e' or 'p' or '\0'. + const char* + find_end_of_float(const char* first, const char* last, const char* digits, + char exp) + { + while (first < last && strchr(digits, *first) != nullptr) + ++first; + if (first < last && *first == '.') + { + ++first; + while (first < last && strchr(digits, *first)) + ++first; + } + if (first < last && exp != 0 && std::tolower((unsigned char)*first) == exp) + { + ++first; + if (first < last && (*first == '-' || *first == '+')) + ++first; + while (first < last && strchr(dec_digits, *first) != nullptr) + ++first; + } + return first; + } + + // Determine the prefix of [first, last) that matches the pattern + // corresponding to `fmt`. + // Returns a NTBS containing the pattern, using `buf` to allocate + // additional storage if needed. + // Returns a nullptr if a valid pattern is not present. + const char* + pattern(const char* const first, const char* last, + chars_format& fmt, pmr::string& buf) + { + // fmt has the value of one of the enumerators of chars_format. + __glibcxx_assert(valid_fmt(fmt)); + + string_view res; + + if (first == last || *first == '+') [[unlikely]] + return nullptr; + + const int neg = (*first == '-'); + + if (std::memchr("iInN", (unsigned char)first[neg], 4)) + { + ptrdiff_t len = last - first; + if (len < (3 + neg)) + return nullptr; + + // possible infinity or NaN, let strtod decide + if (first[neg] == 'i' || first[neg] == 'I') + { + // Need at most 9 chars for "-INFINITY", ignore anything after it. + len = std::min(len, ptrdiff_t(neg + 8)); + } + else if (len > (neg + 3) && first[neg + 3] == '(') + { + // Look for end of "NAN(n-char-sequence)" + if (void* p = std::memchr(const_cast(first)+4, ')', len-4)) + len = static_cast(p) + 1 - first; +#ifndef __cpp_exceptions + if (len > buffer_resource::guaranteed_capacity()) + { + // The character sequence is too large for the buffer. + // Allocation failure could terminate the process, + // so just return an error via the fmt parameter. + fmt = chars_format{}; + return nullptr; + } +#endif + } + else // Only need 4 chars for "-NAN" + len = neg + 3; + + buf.assign(first, 0, len); + // prevent make_result correcting for "0x" + fmt = chars_format::general; + return buf.c_str(); + } + + const char* digits; + char* ptr; + + // Assign [first,last) to a std::string to get a NTBS that can be used + // with strspn, strtod etc. + // If the string would be longer than the fixed buffer inside the + // buffer_resource type use find_end_of_float to try to reduce how + // much memory is needed, to reduce the chance of std::bad_alloc. + + if (fmt == chars_format::hex) + { + digits = hex_digits; + + if ((last - first + 2) > buffer_resource::guaranteed_capacity()) + { + last = find_end_of_float(first + neg, last, digits, 'p'); +#ifndef __cpp_exceptions + if ((last - first + 2) > buffer_resource::guaranteed_capacity()) + { + // The character sequence is still too large for the buffer. + // Allocation failure could terminate the process, + // so just return an error via the fmt parameter. + fmt = chars_format{}; + return nullptr; + } +#endif + } + + buf = "-0x" + !neg; + buf.append(first + neg, last); + ptr = buf.data() + neg + 2; + } + else + { + digits = dec_digits; + + if ((last - first) > buffer_resource::guaranteed_capacity()) + { + last = find_end_of_float(first + neg, last, digits, + "e"[fmt == chars_format::fixed]); +#ifndef __cpp_exceptions + if ((last - first) > buffer_resource::guaranteed_capacity()) + { + // The character sequence is still too large for the buffer. + // Allocation failure could terminate the process, + // so just return an error via the fmt parameter. + fmt = chars_format{}; + return nullptr; + } +#endif + } + buf.assign(first, last); + ptr = buf.data() + neg; + } + + // "A non-empty sequence of decimal digits" or + // "A non-empty sequence of hexadecimal digits" + size_t len = std::strspn(ptr, digits); + // "possibly containing a radix character," + if (ptr[len] == '.') + { + const size_t len2 = std::strspn(ptr + len + 1, digits); + if (len + len2) + ptr += len + 1 + len2; + else + return nullptr; + } + else if (len == 0) [[unlikely]] + return nullptr; + else + ptr += len; + + if (fmt == chars_format::fixed) + { + // Truncate the string to stop strtod parsing past this point. + *ptr = '\0'; + } + else if (fmt == chars_format::scientific) + { + // Check for required exponent part which starts with 'e' or 'E' + if (*ptr != 'e' && *ptr != 'E') + return nullptr; + // then an optional plus or minus sign + const int sign = (ptr[1] == '-' || ptr[1] == '+'); + // then a nonempty sequence of decimal digits + if (!std::memchr(dec_digits, (unsigned char)ptr[1+sign], 10)) + return nullptr; + } + else if (fmt == chars_format::general) + { + if (*ptr == 'x' || *ptr == 'X') + *ptr = '\0'; + } + + return buf.c_str(); + } + + // Convert the NTBS `str` to a floating-point value of type `T`. + // If `str` cannot be converted, `value` is unchanged and `0` is returned. + // Otherwise, let N be the number of characters consumed from `str`. + // On success `value` is set to the converted value and N is returned. + // If the converted value is out of range, `value` is unchanged and + // -N is returned. + template + ptrdiff_t + from_chars_impl(const char* str, T& value, errc& ec) noexcept + { + if (locale_t loc = ::newlocale(LC_ALL, "C", (locale_t)0)) [[likely]] + { + locale_t orig = ::uselocale(loc); + + const int save_errno = errno; + errno = 0; + char* endptr; + T tmpval; + if constexpr (is_same_v) + tmpval = std::strtof(str, &endptr); + if constexpr (is_same_v) + tmpval = std::strtod(str, &endptr); + else if constexpr (is_same_v) + tmpval = std::strtold(str, &endptr); + const int conv_errno = std::__exchange(errno, save_errno); + + ::uselocale(orig); + ::freelocale(loc); + + const ptrdiff_t n = endptr - str; + if (conv_errno == ERANGE) [[unlikely]] + { + if (std::isinf(tmpval)) // overflow + ec = errc::result_out_of_range; + else // underflow (LWG 3081 wants to set value = tmpval here) + ec = errc::result_out_of_range; + } + else if (n) + { + value = tmpval; + ec = errc(); + } + return n; + } + else if (errno == ENOMEM) + ec = errc::not_enough_memory; + + return 0; + } + + inline from_chars_result + make_result(const char* str, ptrdiff_t n, chars_format fmt, errc ec) noexcept + { + from_chars_result result = { str, ec }; + if (n != 0) + { + if (fmt == chars_format::hex) + n -= 2; // correct for the "0x" inserted into the pattern + result.ptr += n; + } + else if (fmt == chars_format{}) [[unlikely]] + { + // FIXME: the standard does not allow this result. + ec = errc::not_enough_memory; + } + return result; + } + +} // namespace + +// FIXME: This should be reimplemented so it doesn't use strtod and newlocale. +// That will avoid the need for any memory allocation, meaning that the +// non-conforming errc::not_enough_memory result cannot happen. + +from_chars_result +from_chars(const char* first, const char* last, float& value, + chars_format fmt) noexcept +{ + buffer_resource mr; + pmr::string buf(&mr); + size_t len = 0; + errc ec = errc::invalid_argument; + __try + { + if (const char* pat = pattern(first, last, fmt, buf)) [[likely]] + len = from_chars_impl(pat, value, ec); + } + __catch (const std::bad_alloc&) + { + fmt = chars_format{}; + } + return make_result(first, len, fmt, ec); +} + +from_chars_result +from_chars(const char* first, const char* last, double& value, + chars_format fmt) noexcept +{ + buffer_resource mr; + pmr::string buf(&mr); + size_t len = 0; + errc ec = errc::invalid_argument; + __try + { + if (const char* pat = pattern(first, last, fmt, buf)) [[likely]] + len = from_chars_impl(pat, value, ec); + } + __catch (const std::bad_alloc&) + { + fmt = chars_format{}; + } + return make_result(first, len, fmt, ec); +} + +from_chars_result +from_chars(const char* first, const char* last, long double& value, + chars_format fmt) noexcept +{ + buffer_resource mr; + pmr::string buf(&mr); + size_t len = 0; + errc ec = errc::invalid_argument; + __try + { + if (const char* pat = pattern(first, last, fmt, buf)) [[likely]] + len = from_chars_impl(pat, value, ec); + } + __catch (const std::bad_alloc&) + { + fmt = chars_format{}; + } + return make_result(first, len, fmt, ec); +} + +#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT +extern "C" from_chars_result +_ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last, + long double& value, + chars_format fmt) noexcept +__attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format"))); +#endif + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // _GLIBCXX_HAVE_USELOCALE diff --git a/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc b/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc index 8454b304d13..b3ca6525681 100644 --- a/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc +++ b/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc @@ -41,3 +41,4 @@ test01(const char* first, const char* last) } // { dg-prune-output "enable_if" } +// { dg-prune-output "cannot bind non-const lvalue reference" } diff --git a/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc b/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc index 12b5e597b9f..0d2fe2b3e65 100644 --- a/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc +++ b/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc @@ -44,3 +44,4 @@ test01(const char* first, const char* last) } // { dg-prune-output "enable_if" } +// { dg-prune-output "cannot bind non-const lvalue reference" } diff --git a/libstdc++-v3/testsuite/20_util/from_chars/2.cc b/libstdc++-v3/testsuite/20_util/from_chars/2.cc index 902092fd423..e5e2db82c3f 100644 --- a/libstdc++-v3/testsuite/20_util/from_chars/2.cc +++ b/libstdc++-v3/testsuite/20_util/from_chars/2.cc @@ -55,6 +55,12 @@ test01() VERIFY( r.ptr == s.data() ); VERIFY( i == 999 ); + s = "+1"; + r = std::from_chars(s.data(), s.data() + s.length(), i); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( i == 999 ); + unsigned u = 888; s = "-1"; r = std::from_chars(s.data(), s.data() + s.length(), u); @@ -69,6 +75,11 @@ test01() VERIFY( r.ec == std::errc::invalid_argument ); VERIFY( r.ptr == s.data() ); VERIFY( u == 888 ); + s = "+1"; + r = std::from_chars(s.data(), s.data() + s.length(), u); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( u == 888 ); for (int base = 2; base <= 36; ++base) { diff --git a/libstdc++-v3/testsuite/20_util/from_chars/4.cc b/libstdc++-v3/testsuite/20_util/from_chars/4.cc new file mode 100644 index 00000000000..6d692592e95 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/from_chars/4.cc @@ -0,0 +1,368 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// is supported in C++14 as a GNU extension +// { dg-do run { target c++14 } } + +#include +#include +#include +#include +#include +#include + +// Test std::from_chars floating-point conversions. + +void +test01() +{ + std::string s; + double d; + std::from_chars_result res; + + for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific, + std::chars_format::general, std::chars_format::hex }) + { + s = "Info"; + res = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( std::isinf(d) ); + VERIFY( res.ptr == s.data() + 3 ); + VERIFY( res.ec == std::errc{} ); + + s = "-INFIN"; + res = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( std::isinf(d) ); + VERIFY( d < 0 ); + VERIFY( res.ptr == s.data() + 4 ); + VERIFY( res.ec == std::errc{} ); + + s = "InFiNiTy aNd BeYoNd"; + res = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( std::isinf(d) ); + VERIFY( res.ptr == s.data() + 8 ); + VERIFY( res.ec == std::errc{} ); + + s = "nAn"; + res = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( std::isnan(d) ); + VERIFY( res.ptr == s.data() + 3 ); + VERIFY( res.ec == std::errc{} ); + + s = "-NAN()"; + res = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( std::isnan(d) ); + VERIFY( res.ptr == s.data() + s.length() ); + VERIFY( res.ec == std::errc{} ); + } +} + +void +test02() +{ + std::string s; + double d = 1.0; + std::from_chars_result res; + + s = "0x123"; + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( d == 0.0 ); + VERIFY( res.ptr == s.data() + 1 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::fixed); + VERIFY( d == 0.0 ); + VERIFY( res.ptr == s.data() + 1 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::scientific); + VERIFY( d == 1.0 ); + VERIFY( res.ptr == s.data() ); + VERIFY( res.ec == std::errc::invalid_argument ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::general); + VERIFY( d == 0.0 ); + VERIFY( res.ptr == s.data() + 1 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::hex); + VERIFY( d == 0.0 ); + VERIFY( res.ptr == s.data() + 1 ); + VERIFY( res.ec == std::errc{} ); +} + +void +test03() +{ + std::string s; + double d = 1.0; + std::from_chars_result res; + + s = "0.5e+2azzz"; + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( d == 0.5e+2 ); + VERIFY( res.ptr == s.data() + s.length() - 1 - 3 ); + VERIFY( res.ec == std::errc{} ); + + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::fixed); + VERIFY( d == 0.5 ); + VERIFY( res.ptr == s.data() + 3 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::scientific); + VERIFY( d == 0.5e+2 ); + VERIFY( res.ptr == s.data() + s.length() - 1 - 3 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::general); + VERIFY( d == 0.5e+2 ); + VERIFY( res.ptr == s.data() + s.length() - 1 - 3 ); + VERIFY( res.ec == std::errc{} ); + + d = 1.0; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::hex); + VERIFY( d == 0x0.5Ep0 ); + VERIFY( res.ptr == s.data() + 4 ); + VERIFY( res.ec == std::errc{} ); + + s = "1.Ap-2zzz"; + res = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::hex); + VERIFY( d == 0.40625 ); + VERIFY( res.ptr == s.data() + s.length() - 3 ); + VERIFY( res.ec == std::errc{} ); +} + +void +test04() +{ + // Huge input strings + std::string s(1000, '0'); + double d = 1.0; + std::from_chars_result res; + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( res.ptr == s.data() + s.length() ); + VERIFY( res.ec == std::errc{} ); + VERIFY( d == 0.0 ); + + s += ".5"; + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( res.ptr == s.data() + s.length() ); + VERIFY( res.ec == std::errc{} ); + VERIFY( d == 0.5 ); + + s += "e2"; + auto len = s.length(); + s += std::string(1000, 'a'); + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( res.ptr == s.data() + len ); + VERIFY( res.ec == std::errc{} ); + VERIFY( d == 50 ); +} + +using std::to_string; + +#ifdef __GLIBCXX_TYPE_INT_N_0 +std::string +to_string(unsigned __GLIBCXX_TYPE_INT_N_0 val) +{ + using Limits = std::numeric_limits; + std::string s(Limits::digits10+2, '0'); + for (auto iter = s.end(); val != 0; val /= 10) + *--iter = '0' + (val % 10); + return s; +} +#endif + +void +test05() +{ + std::from_chars_result res; + float flt; + double dbl; + long double ldbl; + + // Small integer values that are exactly representable + + for (int i = 0; i < 100; ++i) + { + std::string s = to_string(i); + int len = s.length(); + s += "123"; + const char* s1 = s.c_str(); + const char* s1_end = s1 + len; + + for (auto fmt : { std::chars_format::fixed, + std::chars_format::general, + std::chars_format::hex }) + { + if (fmt == std::chars_format::hex && i > 9) + continue; + + res = std::from_chars(s1, s1_end, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s1_end ); + VERIFY( flt == i ); + + res = std::from_chars(s1, s1_end, dbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s1_end ); + VERIFY( dbl == i ); + + res = std::from_chars(s1, s1_end, ldbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s1_end ); + VERIFY( ldbl == i ); + } + + if (i > 9) + continue; + + // Test single-digit integers with small exponents. + + const char s2[] = { '.', *s1, 'e', '0', '0', '0', '1' }; + const char* s2_end = s2 + sizeof(s2); + + const char s3[] = { *s1, '0', 'e', '-', '0', '0', '1' }; + const char* s3_end = s3 + sizeof(s3); + + for (auto fmt : { std::chars_format::scientific, + std::chars_format::general }) + { + res = std::from_chars(s2, s2_end, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s2_end ); + VERIFY( flt == i ); + + res = std::from_chars(s3, s3_end, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s3_end ); + VERIFY( flt == i ); + + res = std::from_chars(s2, s2_end, dbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s2_end ); + VERIFY( dbl == i ); + + res = std::from_chars(s3, s3_end, dbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s3_end ); + VERIFY( dbl == i ); + + res = std::from_chars(s2, s2_end, ldbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s2_end ); + VERIFY( ldbl == i ); + + res = std::from_chars(s3, s3_end, ldbl, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s3_end ); + VERIFY( ldbl == i ); + } + } +} + +template +void +test_max_mantissa() +{ + using Float_limits = std::numeric_limits; + using UInt_limits = std::numeric_limits; + + if constexpr (Float_limits::is_iec559 + && Float_limits::digits < UInt_limits::digits) + { + std::printf("Testing %d-bit float, using %zu-bit integer\n", + Float_limits::digits + (int)std::log2(Float_limits::max_exponent) + 1, + sizeof(UIntT) * __CHAR_BIT__); + + std::from_chars_result res; + FloatT flt; + + for (int i = 0; i < 10; ++i) + { + // (1 << digits) - 1 is the maximum value of the mantissa + const auto val = ((UIntT)1 << Float_limits::digits) - 1 - i; + std::string s = to_string(val); + auto len = s.length(); + s += "000"; // these should be ignored + for (auto fmt : { std::chars_format::fixed, + std::chars_format::general }) + { + res = std::from_chars(s.data(), s.data() + len, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s.data() + len ); + VERIFY( flt == val ); + } + s.resize(len); + const auto orig_len = len; + s += "e+000"; + len = s.length(); + s += "111"; + for (auto fmt : { std::chars_format::scientific, + std::chars_format::general }) + { + res = std::from_chars(s.data(), s.data() + len, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s.data() + len ); + VERIFY( flt == val ); + + std::string s2 = s.substr(0, len - 5); + s2.insert(s2.cbegin() + orig_len - 1, '.'); + s2 += "e000000000001"; + res = std::from_chars(s.data(), s.data() + len, flt, fmt); + VERIFY( res.ec == std::errc{} ); + VERIFY( res.ptr == s.data() + len ); + VERIFY( flt == val ); + } + } + } +} + +void +test06() +{ + test_max_mantissa(); + test_max_mantissa(); +#ifdef __GLIBCXX_TYPE_INT_N_0 + test_max_mantissa(); +#endif +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); +} diff --git a/libstdc++-v3/testsuite/20_util/from_chars/5.cc b/libstdc++-v3/testsuite/20_util/from_chars/5.cc new file mode 100644 index 00000000000..f8fc7f6cd12 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/from_chars/5.cc @@ -0,0 +1,163 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// is supported in C++14 as a GNU extension +// { dg-do run { target c++14 } } + +#include +#include +#include +#include + +// Test std::from_chars error handling. + +void +test01() +{ + std::from_chars_result r; + double d = 3.2; + std::string s; + + for (auto p : { "", "*", ".", "-", "-*", "-.", "+", "+.", "+-", "-+", "+1", + ".p1", "-.p1", + "in", "inch", "+inf", "na", "nam", "+nan" }) + { + s = p; + for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific, + std::chars_format::general, std::chars_format::hex }) + { + r = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( d == 3.2 ); + } + } + + for (auto p : { ".e1", "-.e1" }) // These are valid patterns for hex format + { + s = p; + for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific, + std::chars_format::general }) + { + r = std::from_chars(s.data(), s.data() + s.length(), d, fmt); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( d == 3.2 ); + } + } + + // scientific format requires an exponent + for (auto p : { "1.2", "-1.2", "1.2e", "-1.2e", "1.2e-", "-1.2e+" }) + { + s = p; + r = std::from_chars(s.data(), s.data() + s.length(), d, + std::chars_format::scientific); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( d == 3.2 ); + } + + // patterns that are invalid without the final character + for (auto p : { "1", ".1", "-1", "-.1", + "inf", "-inf", "nan", "-nan" }) + { + s = p; + for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific, + std::chars_format::general, std::chars_format::hex }) + { + r = std::from_chars(s.data(), s.data() + s.length() - 1, d, fmt); + VERIFY( r.ec == std::errc::invalid_argument ); + VERIFY( r.ptr == s.data() ); + VERIFY( d == 3.2 ); + } + } +} + +void +test02() +{ + std::from_chars_result r; + std::string s; + + float f = 0.5; + // Overflow + s = "99999999999999999e999999999999999999"; + r = std::from_chars(s.data(), s.data() + s.length(), f); + VERIFY( r.ec == std::errc::result_out_of_range ); + VERIFY( r.ptr == s.data() + s.length() ); + VERIFY( f == 0.5 ); + + s += '*'; + r = std::from_chars(s.data(), s.data() + s.length(), f); + VERIFY( r.ec == std::errc::result_out_of_range ); + VERIFY( r.ptr == s.data() + s.length() - 1 ); + VERIFY( f == 0.5 ); + + s.insert(s.begin(), '-'); + r = std::from_chars(s.data(), s.data() + s.length(), f); + VERIFY( r.ec == std::errc::result_out_of_range ); + VERIFY( r.ptr == s.data() + s.length() - 1 ); + VERIFY( f == 0.5 ); +} + +void +test03() +{ + double d = 0.5; + // Underflow + std::string s("-1.2345e-9999zzz"); + std::from_chars_result res; + res = std::from_chars(s.data(), s.data() + s.length(), d); + VERIFY( res.ptr == s.data() + s.length() - 3 ); + VERIFY( res.ec == std::errc::result_out_of_range ); + VERIFY( d == 0.5 ); + + res = std::from_chars(s.data() + 1, s.data() + s.length(), d); + VERIFY( res.ptr == s.data() + s.length() - 3 ); + VERIFY( res.ec == std::errc::result_out_of_range ); + VERIFY( d == 0.5 ); +} + +void +test04() +{ + std::from_chars_result res; + std::string z(2000, '0'); + // Invalid inputs for scientific format + for (const char* s : { "", "1", ".", ".0", ".5", "1e+", "1e+-1" }) + { + for (auto len : { 0, 10, 100, 1000, 2000 }) + { + auto str = z.substr(len) + s; + double d = 99.0; + res = std::from_chars(str.data(), str.data() + str.length(), d, + std::chars_format::scientific); + VERIFY( res.ec == std::errc::invalid_argument ); + VERIFY( res.ptr == str.data() ); + VERIFY( d == 99.0 ); + } + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc index aedb6561ed4..fd8224b6789 100644 --- a/libstdc++-v3/testsuite/util/testsuite_abi.cc +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -209,6 +209,8 @@ check_version(symbol& test, bool added) known_versions.push_back("GLIBCXX_3.4.26"); known_versions.push_back("GLIBCXX_3.4.27"); known_versions.push_back("GLIBCXX_3.4.28"); + known_versions.push_back("GLIBCXX_3.4.29"); + known_versions.push_back("GLIBCXX_LDBL_3.4.29"); known_versions.push_back("CXXABI_1.3"); known_versions.push_back("CXXABI_LDBL_1.3"); known_versions.push_back("CXXABI_1.3.1"); @@ -240,7 +242,10 @@ check_version(symbol& test, bool added) test.version_status = symbol::incompatible; // Check that added symbols are added in the latest pre-release version. - bool latestp = (test.version_name == "GLIBCXX_3.4.28" + bool latestp = (test.version_name == "GLIBCXX_3.4.29" + // XXX remove next line when GLIBCXX_3.4.30 is added and baselines + // have been regenerated to include GLIBCXX_LDBL_3.4.29 symbols: + || test.version_name == "GLIBCXX_LDBL_3.4.29" || test.version_name == "CXXABI_1.3.12" || test.version_name == "CXXABI_FLOAT128" || test.version_name == "CXXABI_TM_1");