-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[libc++] Optimize num_get integral functions #121795
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
Open
philnik777
wants to merge
1
commit into
llvm:main
Choose a base branch
from
philnik777:optimize_num_get
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
You can test this locally with the following command:git-clang-format --diff HEAD~1 HEAD --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/num.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/src/locale.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_int.pass.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long.pass.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long_long.pass.cpp libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_short.pass.cpp View the diff from clang-format here.diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index c2db129bd..61de6f5b3 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -85,7 +85,7 @@ struct __num_get : protected __num_get_base {
_CharT* __atoms);
_LIBCPP_HIDE_FROM_ABI static ptrdiff_t __atoms_offset(const _CharT* __atoms, _CharT __val) {
-# if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
+# if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
if constexpr (is_same<_CharT, char>::value) {
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi") // This should be removed once https://llvm.org/PR128361 is fixed
@@ -98,7 +98,7 @@ struct __num_get : protected __num_get_base {
return std::min(__int_chr_cnt, std::__find_first_set(__res));
_LIBCPP_DIAGNOSTIC_POP
}
-# endif
+# endif
return std::find(__atoms, __atoms + __int_chr_cnt, __val) - __atoms;
}
@@ -193,9 +193,9 @@ int __num_get<_CharT>::__stage2_float_loop(
}
extern template struct _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __num_get<char>;
-# if _LIBCPP_HAS_WIDE_CHARACTERS
+# if _LIBCPP_HAS_WIDE_CHARACTERS
extern template struct _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __num_get<wchar_t>;
-# endif
+# endif
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp __do_strtod(const char* __a, char** __p2);
@@ -618,9 +618,9 @@ _InputIterator num_get<_CharT, _InputIterator>::do_get(
}
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS num_get<char>;
-# if _LIBCPP_HAS_WIDE_CHARACTERS
+# if _LIBCPP_HAS_WIDE_CHARACTERS
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS num_get<wchar_t>;
-# endif
+# endif
struct _LIBCPP_EXPORTED_FROM_ABI __num_put_base {
protected:
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 6bf38bd8c..ccbd096e2 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";
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_int.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_int.pass.cpp
index f9cef08e2..1361404a4 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_int.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_int.pass.cpp
@@ -69,15 +69,13 @@ int main(int, char**)
assert(v == 1);
}
{
- const char str[] = "-1";
- 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+sizeof(str)-1);
- assert(err == ios.goodbit);
- assert(v == std::numeric_limits<unsigned int>::max());
+ const char str[] = "-1";
+ 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 + sizeof(str) - 1);
+ assert(err == ios.goodbit);
+ assert(v == std::numeric_limits<unsigned int>::max());
}
std::hex(ios);
{
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long.pass.cpp
index fed6fc024..a49904a64 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long.pass.cpp
@@ -69,15 +69,13 @@ int main(int, char**)
assert(v == 1);
}
{
- const char str[] = "-1";
- 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+sizeof(str)-1);
- assert(err == ios.goodbit);
- assert(v == std::numeric_limits<unsigned long>::max());
+ const char str[] = "-1";
+ 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 + sizeof(str) - 1);
+ assert(err == ios.goodbit);
+ assert(v == std::numeric_limits<unsigned long>::max());
}
std::hex(ios);
{
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long_long.pass.cpp
index 0bdb6c1c3..b6dade2f3 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_long_long.pass.cpp
@@ -69,15 +69,13 @@ int main(int, char**)
assert(v == 1);
}
{
- const char str[] = "-1";
- 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+sizeof(str)-1);
- assert(err == ios.goodbit);
- assert(v == std::numeric_limits<unsigned long long>::max());
+ const char str[] = "-1";
+ 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 + sizeof(str) - 1);
+ assert(err == ios.goodbit);
+ assert(v == std::numeric_limits<unsigned long long>::max());
}
std::hex(ios);
{
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_short.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_short.pass.cpp
index decfbe943..f1ad05ccf 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_short.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_unsigned_short.pass.cpp
@@ -69,15 +69,13 @@ int main(int, char**)
assert(v == 1);
}
{
- const char str[] = "-1";
- 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+sizeof(str)-1);
- assert(err == ios.goodbit);
- assert(v == std::numeric_limits<unsigned short>::max());
+ const char str[] = "-1";
+ 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 + sizeof(str) - 1);
+ assert(err == ios.goodbit);
+ assert(v == std::numeric_limits<unsigned short>::max());
}
std::hex(ios);
{
|
0b82a8c
to
b466dc6
Compare
4a99b3e
to
ae3fb04
Compare
@llvm/pr-subscribers-libcxx Author: Nikolas Klauser (philnik777) Changes
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:
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]
|
ldionne
requested changes
Feb 26, 2025
...on/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
Show resolved
Hide resolved
0ffa10e
to
9f61a94
Compare
9f61a94
to
ea90f16
Compare
ea90f16
to
0512ce1
Compare
0512ce1
to
a6c2c52
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This patch applies multiple optimizations:
This avoids allocations, removes the need for strto{,u}ll and avoids __stage2_int_loop (avoiding extra writes to memory)