Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] Optimize num_get integral functions #121795

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

philnik777
Copy link
Contributor

@philnik777 philnik777 commented Jan 6, 2025

---------------------------------------------------
Benchmark                            old        new
---------------------------------------------------
BM_num_get<bool>                 86.5 ns    32.3 ns
BM_num_get<long>                 82.1 ns    30.3 ns
BM_num_get<long long>            85.2 ns    33.4 ns
BM_num_get<unsigned short>       85.3 ns    31.2 ns
BM_num_get<unsigned int>         84.2 ns    31.1 ns
BM_num_get<unsigned long>        83.6 ns    31.9 ns
BM_num_get<unsigned long long>   87.7 ns    31.5 ns
BM_num_get<float>                 116 ns     114 ns
BM_num_get<double>                114 ns     114 ns
BM_num_get<long double>           113 ns     114 ns
BM_num_get<void*>                 151 ns     144 ns

Copy link

github-actions bot commented Jan 6, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff ec9c2935e19171ce8004e1d970f9b7bf068d92a7 0ffa10e1e2325d91d114f0b180a01478cfea7cb8 --extensions ,h,cpp -- libcxx/include/__algorithm/simd_utils.h libcxx/include/__locale_dir/locale_base_api.h libcxx/include/__locale_dir/locale_base_api/bsd_locale_fallbacks.h libcxx/include/__locale_dir/locale_base_api/ibm.h libcxx/include/__locale_dir/support/bsd_like.h libcxx/include/__locale_dir/support/fuchsia.h libcxx/include/__locale_dir/support/linux.h libcxx/include/__locale_dir/support/no_locale/strtonum.h libcxx/include/__locale_dir/support/windows.h libcxx/include/__support/xlocale/__strtonum_fallback.h libcxx/include/locale libcxx/src/locale.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
View the diff from clang-format here.
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
index 6bf38bd8c7..ccbd096e27 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
@@ -103,16 +103,14 @@ int main(int, char**)
         assert(v == 291);
     }
     {
-        const char str[] = "a123";
-        std::dec(ios);
-        std::ios_base::iostate err = ios.goodbit;
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str),
-                  cpp17_input_iterator<const char*>(str+sizeof(str)),
-                  ios, err, v);
-        assert(base(iter) == str);
-        assert(err == ios.failbit);
-        assert(v == 0);
+      const char str[] = "a123";
+      std::dec(ios);
+      std::ios_base::iostate err             = ios.goodbit;
+      cpp17_input_iterator<const char*> iter = f.get(
+          cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
+      assert(base(iter) == str);
+      assert(err == ios.failbit);
+      assert(v == 0);
     }
     {
         const char str[] = "0x123";

@philnik777 philnik777 force-pushed the optimize_num_get branch 2 times, most recently from 0b82a8c to b466dc6 Compare January 6, 2025 16:56
@philnik777 philnik777 force-pushed the optimize_num_get branch 3 times, most recently from 4a99b3e to ae3fb04 Compare January 30, 2025 20:06
@philnik777 philnik777 marked this pull request as ready for review February 26, 2025 10:44
@philnik777 philnik777 requested a review from a team as a code owner February 26, 2025 10:44
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 26, 2025

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes
---------------------------------------------------
Benchmark                            old        new
---------------------------------------------------
BM_num_get&lt;bool&gt;                 86.5 ns    32.3 ns
BM_num_get&lt;long&gt;                 82.1 ns    30.3 ns
BM_num_get&lt;long long&gt;            85.2 ns    33.4 ns
BM_num_get&lt;unsigned short&gt;       85.3 ns    31.2 ns
BM_num_get&lt;unsigned int&gt;         84.2 ns    31.1 ns
BM_num_get&lt;unsigned long&gt;        83.6 ns    31.9 ns
BM_num_get&lt;unsigned long long&gt;   87.7 ns    31.5 ns
BM_num_get&lt;float&gt;                 116 ns     114 ns
BM_num_get&lt;double&gt;                114 ns     114 ns
BM_num_get&lt;long double&gt;           113 ns     114 ns
BM_num_get&lt;void*&gt;                 151 ns     144 ns

Patch is 20.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/121795.diff

7 Files Affected:

  • (modified) libcxx/docs/ReleaseNotes/20.rst (+2)
  • (modified) libcxx/include/__algorithm/simd_utils.h (+25)
  • (modified) libcxx/include/__locale_dir/locale_base_api.h (-11)
  • (modified) libcxx/include/__locale_dir/support/bsd_like.h (-9)
  • (modified) libcxx/include/__locale_dir/support/windows.h (-8)
  • (modified) libcxx/include/locale (+152-153)
  • (modified) libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp (+12)
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 57ab0c167544b..d8e275020f6f4 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -120,6 +120,8 @@ Improvements and New Features
 
 - Added :ref:`hardening mode <hardening>` support for ``forward_list`` and ``bitset``.
 
+- The ``num_get::do_get`` integral overloads have been optimized. resulting in a performance improvement of up to 2.8x.
+
 Deprecations and Removals
 -------------------------
 
diff --git a/libcxx/include/__algorithm/simd_utils.h b/libcxx/include/__algorithm/simd_utils.h
index 4e03723a32854..987cf09cf48f7 100644
--- a/libcxx/include/__algorithm/simd_utils.h
+++ b/libcxx/include/__algorithm/simd_utils.h
@@ -116,11 +116,36 @@ template <class _VecT, class _Iter>
   }(make_index_sequence<__simd_vector_size_v<_VecT>>{});
 }
 
+// Load the first _Np elements, zero the rest
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+template <class _VecT, size_t _Np, class _Iter>
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT __partial_load(_Iter __iter) noexcept {
+  return [=]<size_t... _LoadIndices, size_t... _ZeroIndices>(
+             index_sequence<_LoadIndices...>, index_sequence<_ZeroIndices...>) _LIBCPP_ALWAYS_INLINE noexcept {
+    return _VecT{__iter[_LoadIndices]...};
+  }(make_index_sequence<_Np>{}, make_index_sequence<__simd_vector_size_v<_VecT> - _Np>{});
+}
+
+template <class _VecT>
+[[__nodiscard__]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _VecT
+__broadcast(__simd_vector_underlying_type_t<_VecT> __val) {
+  return [&]<std::size_t... _Indices>(index_sequence<_Indices...>) {
+    return _VecT{((void)_Indices, __val)...};
+  }(make_index_sequence<__simd_vector_size_v<_VecT>>());
+}
+_LIBCPP_DIAGNOSTIC_POP
+
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __all_of(__simd_vector<_Tp, _Np> __vec) noexcept {
   return __builtin_reduce_and(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
 }
 
+template <class _Tp, size_t _Np>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI bool __none_of(__simd_vector<_Tp, _Np> __vec) noexcept {
+  return !__builtin_reduce_or(__vec);
+}
+
 template <class _Tp, size_t _Np>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI size_t __find_first_set(__simd_vector<_Tp, _Np> __vec) noexcept {
   using __mask_vec = __simd_vector<bool, _Np>;
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index bbee9f49867fd..efe1e9fae44ac 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -51,8 +51,6 @@
 //  float               __strtof(const char*, char**, __locale_t);
 //  double              __strtod(const char*, char**, __locale_t);
 //  long double         __strtold(const char*, char**, __locale_t);
-//  long long           __strtoll(const char*, char**, __locale_t);
-//  unsigned long long  __strtoull(const char*, char**, __locale_t);
 // }
 //
 // Character manipulation functions
@@ -182,15 +180,6 @@ inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __
   return strtold_l(__nptr, __endptr, __loc);
 }
 
-inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return strtoll_l(__nptr, __endptr, __base, __loc);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI unsigned long long
-__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return strtoull_l(__nptr, __endptr, __base, __loc);
-}
-
 //
 // Character manipulation functions
 //
diff --git a/libcxx/include/__locale_dir/support/bsd_like.h b/libcxx/include/__locale_dir/support/bsd_like.h
index c0080b13a08cf..6ebf7726f0fab 100644
--- a/libcxx/include/__locale_dir/support/bsd_like.h
+++ b/libcxx/include/__locale_dir/support/bsd_like.h
@@ -75,15 +75,6 @@ inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __
   return ::strtold_l(__nptr, __endptr, __loc);
 }
 
-inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return ::strtoll_l(__nptr, __endptr, __base, __loc);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI unsigned long long
-__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return ::strtoull_l(__nptr, __endptr, __base, __loc);
-}
-
 //
 // Character manipulation functions
 //
diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
index ff89d3e87eb44..c3b24ca013181 100644
--- a/libcxx/include/__locale_dir/support/windows.h
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -184,14 +184,6 @@ inline _LIBCPP_HIDE_FROM_ABI double __strtod(const char* __nptr, char** __endptr
   return ::_strtod_l(__nptr, __endptr, __loc);
 }
 
-inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return ::_strtoi64_l(__nptr, __endptr, __base, __loc);
-}
-inline _LIBCPP_HIDE_FROM_ABI unsigned long long
-__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
-  return ::_strtoui64_l(__nptr, __endptr, __base, __loc);
-}
-
 //
 // Character manipulation functions
 //
diff --git a/libcxx/include/locale b/libcxx/include/locale
index be0f31cece671..cd06dba8adbe6 100644
--- a/libcxx/include/locale
+++ b/libcxx/include/locale
@@ -198,7 +198,9 @@ template <class charT> class messages_byname;
 #    include <__algorithm/equal.h>
 #    include <__algorithm/find.h>
 #    include <__algorithm/max.h>
+#    include <__algorithm/min.h>
 #    include <__algorithm/reverse.h>
+#    include <__algorithm/simd_utils.h>
 #    include <__algorithm/unwrap_iter.h>
 #    include <__assert>
 #    include <__iterator/access.h>
@@ -375,7 +377,7 @@ struct _LIBCPP_EXPORTED_FROM_ABI __num_get_base {
   static int __get_base(ios_base&);
   static const char __src[33]; // "0123456789abcdefABCDEFxX+-pPiInN"
   // count of leading characters in __src used for parsing integers ("012..X+-")
-  static const size_t __int_chr_cnt = 26;
+  static inline const size_t __int_chr_cnt = 26;
   // count of leading characters in __src used for parsing floating-point values ("012..-pP")
   static const size_t __fp_chr_cnt = 28;
 };
@@ -403,6 +405,27 @@ struct __num_get : protected __num_get_base {
 
   [[__deprecated__("This exists only for ABI compatibility")]] static string
   __stage2_int_prep(ios_base& __iob, _CharT* __atoms, _CharT& __thousands_sep);
+
+  _LIBCPP_HIDE_FROM_ABI static ptrdiff_t __atoms_offset(const _CharT* __atoms, _CharT __val) {
+#    if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
+    _LIBCPP_DIAGNOSTIC_PUSH
+    _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
+    if constexpr (is_same<_CharT, char>::value) {
+      using __vec   = __simd_vector<char, 32>;
+      __vec __chars = std::__broadcast<__vec>(__val);
+      __vec __cmp   = std::__partial_load<__vec, __int_chr_cnt>(__atoms);
+      auto __res    = __chars == __cmp;
+      if (std::__none_of(__res))
+        return __int_chr_cnt;
+      return std::min(__int_chr_cnt, std::__find_first_set(__res));
+    } else
+      _LIBCPP_DIAGNOSTIC_POP
+#    endif
+      {
+        return std::find(__atoms, __atoms + __int_chr_cnt, __val) - __atoms;
+      }
+  }
+
   static int __stage2_int_loop(
       _CharT __ct,
       int __base,
@@ -476,7 +499,7 @@ int __num_get<_CharT>::__stage2_int_loop(
     }
     return 0;
   }
-  ptrdiff_t __f = std::find(__atoms, __atoms + __int_chr_cnt, __ct) - __atoms;
+  ptrdiff_t __f = __atoms_offset(__atoms, __ct);
   if (__f >= 24)
     return -1;
   switch (__base) {
@@ -637,43 +660,39 @@ protected:
   _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS iter_type
   __do_get_floating_point(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Fp& __v) const;
 
-  template <class _Signed>
-  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS iter_type
-  __do_get_signed(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Signed& __v) const;
-
-  template <class _Unsigned>
+  template <class _MaybeSigned>
   _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS iter_type
-  __do_get_unsigned(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Unsigned& __v) const;
+  __do_get_integral(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _MaybeSigned& __v) const;
 
   virtual iter_type do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, bool& __v) const;
 
   virtual iter_type do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, long& __v) const {
-    return this->__do_get_signed(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type
   do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, long long& __v) const {
-    return this->__do_get_signed(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type
   do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, unsigned short& __v) const {
-    return this->__do_get_unsigned(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type
   do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, unsigned int& __v) const {
-    return this->__do_get_unsigned(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type
   do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, unsigned long& __v) const {
-    return this->__do_get_unsigned(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type
   do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, unsigned long long& __v) const {
-    return this->__do_get_unsigned(__b, __e, __iob, __err, __v);
+    return this->__do_get_integral(__b, __e, __iob, __err, __v);
   }
 
   virtual iter_type do_get(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, float& __v) const {
@@ -695,65 +714,6 @@ protected:
 template <class _CharT, class _InputIterator>
 locale::id num_get<_CharT, _InputIterator>::id;
 
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__num_get_signed_integral(const char* __a, const char* __a_end, ios_base::iostate& __err, int __base) {
-  if (__a != __a_end) {
-    __libcpp_remove_reference_t<decltype(errno)> __save_errno = errno;
-    errno                                                     = 0;
-    char* __p2;
-    long long __ll = __locale::__strtoll(__a, &__p2, __base, _LIBCPP_GET_C_LOCALE);
-    __libcpp_remove_reference_t<decltype(errno)> __current_errno = errno;
-    if (__current_errno == 0)
-      errno = __save_errno;
-    if (__p2 != __a_end) {
-      __err = ios_base::failbit;
-      return 0;
-    } else if (__current_errno == ERANGE || __ll < numeric_limits<_Tp>::min() || numeric_limits<_Tp>::max() < __ll) {
-      __err = ios_base::failbit;
-      if (__ll > 0)
-        return numeric_limits<_Tp>::max();
-      else
-        return numeric_limits<_Tp>::min();
-    }
-    return static_cast<_Tp>(__ll);
-  }
-  __err = ios_base::failbit;
-  return 0;
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _Tp
-__num_get_unsigned_integral(const char* __a, const char* __a_end, ios_base::iostate& __err, int __base) {
-  if (__a != __a_end) {
-    const bool __negate = *__a == '-';
-    if (__negate && ++__a == __a_end) {
-      __err = ios_base::failbit;
-      return 0;
-    }
-    __libcpp_remove_reference_t<decltype(errno)> __save_errno = errno;
-    errno                                                     = 0;
-    char* __p2;
-    unsigned long long __ll = __locale::__strtoull(__a, &__p2, __base, _LIBCPP_GET_C_LOCALE);
-    __libcpp_remove_reference_t<decltype(errno)> __current_errno = errno;
-    if (__current_errno == 0)
-      errno = __save_errno;
-    if (__p2 != __a_end) {
-      __err = ios_base::failbit;
-      return 0;
-    } else if (__current_errno == ERANGE || numeric_limits<_Tp>::max() < __ll) {
-      __err = ios_base::failbit;
-      return numeric_limits<_Tp>::max();
-    }
-    _Tp __res = static_cast<_Tp>(__ll);
-    if (__negate)
-      __res = -__res;
-    return __res;
-  }
-  __err = ios_base::failbit;
-  return 0;
-}
-
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __do_strtod(const char* __a, char** __p2);
 
@@ -822,12 +782,12 @@ _InputIterator num_get<_CharT, _InputIterator>::do_get(
   return __b;
 }
 
-// signed
-
 template <class _CharT, class _InputIterator>
-template <class _Signed>
-_InputIterator num_get<_CharT, _InputIterator>::__do_get_signed(
-    iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Signed& __v) const {
+template <class _MaybeSigned>
+_InputIterator num_get<_CharT, _InputIterator>::__do_get_integral(
+    iter_type __first, iter_type __last, ios_base& __iob, ios_base::iostate& __err, _MaybeSigned& __v) const {
+  using _Unsigned = __make_unsigned_t<_MaybeSigned>;
+
   // Stage 1
   int __base = this->__get_base(__iob);
   // Stage 2
@@ -836,98 +796,137 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_signed(
   char_type __atoms1[__atoms_size];
   const char_type* __atoms = this->__do_widen(__iob, __atoms1);
   string __grouping        = this->__stage2_int_prep(__iob, __thousands_sep);
-  string __buf;
-  __buf.resize(__buf.capacity());
-  char* __a     = &__buf[0];
-  char* __a_end = __a;
   unsigned __g[__num_get_base::__num_get_buf_sz];
   unsigned* __g_end = __g;
   unsigned __dc     = 0;
-  for (; __b != __e; ++__b) {
-    if (__a_end == __a + __buf.size()) {
-      size_t __tmp = __buf.size();
-      __buf.resize(2 * __buf.size());
-      __buf.resize(__buf.capacity());
-      __a     = &__buf[0];
-      __a_end = __a + __tmp;
+
+  if (__first == __last) {
+    __err |= ios_base::eofbit;
+    return __first;
+  }
+
+  while (!__grouping.empty() && *__first == __thousands_sep) {
+    ++__first;
+    if (__g_end - __g < this->__num_get_buf_sz) {
+      *__g_end++ = __dc;
+      __dc       = 0;
     }
-    if (this->__stage2_int_loop(
-            *__b,
-            __base,
-            __a,
-            __a_end,
-            __dc,
-            __thousands_sep,
-            __grouping,
-            __g,
-            __g_end,
-            const_cast<char_type*>(__atoms)))
-      break;
   }
-  if (__grouping.size() != 0 && __g_end - __g < __num_get_base::__num_get_buf_sz)
-    *__g_end++ = __dc;
-  // Stage 3
-  __v = std::__num_get_signed_integral<_Signed>(__a, __a_end, __err, __base);
-  // Digit grouping checked
-  __check_grouping(__grouping, __g, __g_end, __err);
-  // EOF checked
-  if (__b == __e)
+
+  bool __negate = false;
+  // __c == '+' || __c == '-'
+  if (auto __c = *__first; __c == __atoms[24] || __c == __atoms[25]) {
+    __negate = __c == __atoms[25];
+    ++__first;
+  }
+
+  if (__first == __last) {
     __err |= ios_base::eofbit;
-  return __b;
-}
+    return __first;
+  }
 
-// unsigned
+  bool __parsed_num = false;
 
-template <class _CharT, class _InputIterator>
-template <class _Unsigned>
-_InputIterator num_get<_CharT, _InputIterator>::__do_get_unsigned(
-    iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Unsigned& __v) const {
-  // Stage 1
-  int __base = this->__get_base(__iob);
-  // Stage 2
-  char_type __thousands_sep;
-  const int __atoms_size = __num_get_base::__int_chr_cnt;
-  char_type __atoms1[__atoms_size];
-  const char_type* __atoms = this->__do_widen(__iob, __atoms1);
-  string __grouping        = this->__stage2_int_prep(__iob, __thousands_sep);
-  string __buf;
-  __buf.resize(__buf.capacity());
-  char* __a     = &__buf[0];
-  char* __a_end = __a;
-  unsigned __g[__num_get_base::__num_get_buf_sz];
-  unsigned* __g_end = __g;
-  unsigned __dc     = 0;
-  for (; __b != __e; ++__b) {
-    if (__a_end == __a + __buf.size()) {
-      size_t __tmp = __buf.size();
-      __buf.resize(2 * __buf.size());
-      __buf.resize(__buf.capacity());
-      __a     = &__buf[0];
-      __a_end = __a + __tmp;
+  // If we don't have a pre-set base, figure it out
+  if (__base == 0) {
+    auto __c = *__first;
+    // __c == '0'
+    if (__c == __atoms[0]) {
+      ++__first;
+      if (__first == __last) {
+        __err |= ios_base::eofbit;
+        return __first;
+      }
+      // __c2 == 'x' || __c2 == 'X'
+      if (auto __c2 = *__first; __c2 == __atoms[22] || __c2 == __atoms[23]) {
+        __base = 16;
+        ++__first;
+      } else {
+        __base = 8;
+      }
+    } else {
+      __base = 10;
     }
-    if (this->__stage2_int_loop(
-            *__b,
-            __base,
-            __a,
-            __a_end,
-            __dc,
-            __thousands_sep,
-            __grouping,
-            __g,
-            __g_end,
-            const_cast<char_type*>(__atoms)))
+  } else if (__base == 16) {
+    // Try to swallow '0x'
+
+    // *__first == '0'
+    if (*__first == __atoms[0]) {
+      ++__first;
+      if (__first == __last) {
+        __err |= ios_base::eofbit;
+        return __first;
+      }
+      // __c == 'x' || __c == 'X'
+      if (auto __c = *__first; __c == __atoms[22] || __c == __atoms[23])
+        ++__first;
+      else
+        __parsed_num = true; // We only swallowed '0', so we've started to parse a number
+    }
+  }
+
+  // Calculate the actual number
+  _Unsigned __val   = 0;
+  bool __overflowed = false;
+  for (; __first != __last; ++__first) {
+    auto __c = *__first;
+    if (!__grouping.empty() && __c == __thousands_sep) {
+      if (__g_end - __g < this->__num_get_buf_sz) {
+        *__g_end++ = __dc;
+        __dc       = 0;
+      }
+      continue;
+    }
+    auto __offset = this->__atoms_offset(__atoms, __c);
+    if (__offset >= 22)
       break;
+
+    if (__base == 16 && __offset >= 16)
+      __offset -= 6;
+    if (__offset >= __base)
+      break;
+    __overflowed |= __builtin_mul_overflow(__val, __base, &__val) || __builtin_add_overflow(__val, __offset, &__val);
+    __parsed_num = true;
+    ++__dc;
+  }
+
+  if (!__parsed_num) {
+    __err |= ios_base::failbit;
+    __v = 0;
+  } else if (__overflowed) {
+    __err |= ios_base::failbit;
+    __v = is_signed<_MaybeSigned>::value && __negate
+            ? numeric_limits<_MaybeSigned>::min()
+            : numeric_limits<_MaybeSigned>::max();
+  } else if (!__negate) {
+    if (__val > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max())) {
+      __err |= ios_base::failbit;
+      __v = numeric_limits<_MaybeSigned>::max();
+    } else {
+      __v = __val;
+    }
+  } else if (is_signed<_MaybeSigned>::value) {
+    if (__val > static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
+      __err |= ios_base::failbit;
+      __v = numeric_limits<_MaybeSigned>::min();
+    } else if (__val == static_cast<_Unsigned>(numeric_limits<_MaybeSigned>::max()) + 1) {
+      __v = numeric_limits<_MaybeSigned>::min();
+    } else {
+      __v = -__val;
+    }
+  } else {
+    __v = -__val;
   }
+
   if (__grouping.size() != 0 && __g_end - __g < __num_get_base::__num_get_buf_sz)
     *__g_end++ = __dc;
-  // Stage 3
-  __v = std::__num_get_unsigned_integral<_Unsigned>(__a, __a_end, __err, __base);
+
   // Digit grouping checked
   __check_grouping(__grouping, __g, __g_end, __err);
   // EOF checked
-  if (__b == __e)
+  if (__first == __last)
     __err |= ios_base::eofbit;
-  return __b;
+  return __first;
 }
 
 // floating point
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
index 03b74ebf53936..6bf38bd8c72a1 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
@@ -102,6 +102,18 @@ int main(int, char**)
         assert(err == ios.goodbit);
         assert(v == 291);
     }
+    {
+        const char str[] = "a123";
+        std::dec(ios);
+      ...
[truncated]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment: please explain this change, how your new implementation works, etc in the commit message.

@@ -102,6 +102,18 @@ int main(int, char**)
assert(err == ios.goodbit);
assert(v == 291);
}
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're reimplementing this whole thing, I think it would be pretty important to have good test coverage. Perhaps we can look into missing coverage from the MSVC test suite?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants