[libc++] Optimize num_get integral functions (#121795)

```
---------------------------------------------------
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
```

This patch applies multiple optimizations:
- Stages two and three of do_get are merged and a custom integer parser
has been implemented
This avoids allocations, removes the need for strto{,u}ll and avoids
__stage2_int_loop (avoiding extra writes to memory)
- std::find has been replaced with __atoms_offset, which uses vector
instructions to look for a character

Fixes #158100
Fixes #158102
This commit is contained in:
Nikolas Klauser 2025-11-24 16:53:58 +01:00 committed by GitHub
parent 29cfef1880
commit 2bdd1357c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 450 additions and 412 deletions

View File

@ -83,6 +83,8 @@ Improvements and New Features
iterators, resulting in a performance improvement for ``std::deque<short>`` and
``std::join_view<vector<vector<short>>>`` iterators.
- The ``num_get::do_get`` integral overloads have been optimized, resulting in a performance improvement of up to 2.8x.
Deprecations and Removals
-------------------------

View File

@ -520,7 +520,6 @@ set(files
__locale_dir/locale_base_api.h
__locale_dir/locale_base_api/bsd_locale_fallbacks.h
__locale_dir/locale_base_api/ibm.h
__locale_dir/locale_base_api/musl.h
__locale_dir/locale_base_api/openbsd.h
__locale_dir/messages.h
__locale_dir/money.h

View File

@ -114,6 +114,27 @@ 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]..., ((void)_ZeroIndices, 0)...};
}(make_index_sequence<_Np>{}, make_index_sequence<__simd_vector_size_v<_VecT> - _Np>{});
}
// Create a vector where every elements is __val
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 __any_of(__simd_vector<_Tp, _Np> __vec) noexcept {
return __builtin_reduce_or(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
@ -124,6 +145,11 @@ template <class _Tp, size_t _Np>
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(__builtin_convertvector(__vec, __simd_vector<bool, _Np>));
}
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>;

View File

@ -57,8 +57,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
@ -104,7 +102,6 @@
//
// int __snprintf(char*, size_t, __locale_t, const char*, ...); // required by the headers
// int __asprintf(char**, __locale_t, const char*, ...); // required by the headers
// int __sscanf(const char*, __locale_t, const char*, ...); // required by the headers
// }
#if _LIBCPP_HAS_LOCALIZATION
@ -131,8 +128,6 @@
# include <__locale_dir/locale_base_api/ibm.h>
# elif defined(__OpenBSD__)
# include <__locale_dir/locale_base_api/openbsd.h>
# elif defined(__wasi__) || _LIBCPP_HAS_MUSL_LIBC
# include <__locale_dir/locale_base_api/musl.h>
# endif
# include <__locale_dir/locale_base_api/bsd_locale_fallbacks.h>
@ -192,15 +187,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
//
@ -299,11 +285,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __
char** __s, __locale_t __loc, const char* __format, _Args&&... __args) {
return std::__libcpp_asprintf_l(__s, __loc, __format, std::forward<_Args>(__args)...);
}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
const char* __s, __locale_t __loc, const char* __format, _Args&&... __args) {
return std::__libcpp_sscanf_l(__s, __loc, __format, std::forward<_Args>(__args)...);
}
_LIBCPP_DIAGNOSTIC_POP
# undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT

View File

@ -125,16 +125,6 @@ inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __libcpp_asprintf_l(
return __res;
}
inline _LIBCPP_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __libcpp_sscanf_l(
const char* __s, locale_t __l, const char* __format, ...) {
va_list __va;
va_start(__va, __format);
__locale_guard __current(__l);
int __res = vsscanf(__s, __format, __va);
va_end(__va);
return __res;
}
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_BSD_LOCALE_FALLBACKS_H

View File

@ -53,11 +53,6 @@ private:
// The following are not POSIX routines. These are quick-and-dirty hacks
// to make things pretend to work
inline _LIBCPP_HIDE_FROM_ABI long long strtoll_l(const char* __nptr, char** __endptr, int __base, locale_t locale) {
__setAndRestore __newloc(locale);
return ::strtoll(__nptr, __endptr, __base);
}
inline _LIBCPP_HIDE_FROM_ABI double strtod_l(const char* __nptr, char** __endptr, locale_t locale) {
__setAndRestore __newloc(locale);
return ::strtod(__nptr, __endptr);
@ -73,12 +68,6 @@ inline _LIBCPP_HIDE_FROM_ABI long double strtold_l(const char* __nptr, char** __
return ::strtold(__nptr, __endptr);
}
inline _LIBCPP_HIDE_FROM_ABI unsigned long long
strtoull_l(const char* __nptr, char** __endptr, int __base, locale_t locale) {
__setAndRestore __newloc(locale);
return ::strtoull(__nptr, __endptr, __base);
}
inline _LIBCPP_HIDE_FROM_ABI
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 2, 0) int vasprintf(char** strp, const char* fmt, va_list ap) {
const size_t buff_size = 256;

View File

@ -1,31 +0,0 @@
// -*- C++ -*-
//===-----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This adds support for the extended locale functions that are currently
// missing from the Musl C library.
//
// This only works when the specified locale is "C" or "POSIX", but that's
// about as good as we can do without implementing full xlocale support
// in Musl.
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_MUSL_H
#define _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_MUSL_H
#include <cstdlib>
#include <cwchar>
inline _LIBCPP_HIDE_FROM_ABI long long strtoll_l(const char* __nptr, char** __endptr, int __base, locale_t) {
return ::strtoll(__nptr, __endptr, __base);
}
inline _LIBCPP_HIDE_FROM_ABI unsigned long long strtoull_l(const char* __nptr, char** __endptr, int __base, locale_t) {
return ::strtoull(__nptr, __endptr, __base);
}
#endif // _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_MUSL_H

View File

@ -12,6 +12,7 @@
#include <__algorithm/copy.h>
#include <__algorithm/find.h>
#include <__algorithm/reverse.h>
#include <__algorithm/simd_utils.h>
#include <__charconv/to_chars_integral.h>
#include <__charconv/traits.h>
#include <__config>
@ -48,9 +49,9 @@ 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;
static inline const size_t __fp_chr_cnt = 28;
};
template <class _CharT>
@ -73,7 +74,8 @@ 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);
static int __stage2_int_loop(
[[__deprecated__("This exists only for ABI compatibility")]] static int __stage2_int_loop(
_CharT __ct,
int __base,
char* __a,
@ -85,11 +87,24 @@ struct __num_get : protected __num_get_base {
unsigned*& __g_end,
_CharT* __atoms);
_LIBCPP_HIDE_FROM_ABI static string __stage2_int_prep(ios_base& __iob, _CharT& __thousands_sep) {
locale __loc = __iob.getloc();
const numpunct<_CharT>& __np = use_facet<numpunct<_CharT> >(__loc);
__thousands_sep = __np.thousands_sep();
return __np.grouping();
_LIBCPP_HIDE_FROM_ABI static ptrdiff_t __atoms_offset(const _CharT* __atoms, _CharT __val) {
// TODO: Remove the manual vectorization once https://llvm.org/PR168551 is resolved
# if _LIBCPP_HAS_ALGORITHM_VECTOR_UTILS
if constexpr (is_same<_CharT, char>::value) {
// TODO(LLVM 24): This can be removed, since -Wpsabi doesn't warn on [[gnu::always_inline]] functions anymore.
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wpsabi")
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));
_LIBCPP_DIAGNOSTIC_POP
}
# endif
return std::find(__atoms, __atoms + __int_chr_cnt, __val) - __atoms;
}
_LIBCPP_HIDE_FROM_ABI const _CharT* __do_widen(ios_base& __iob, _CharT* __atoms) const {
@ -122,54 +137,6 @@ string __num_get<_CharT>::__stage2_float_prep(
return __np.grouping();
}
template <class _CharT>
int __num_get<_CharT>::__stage2_int_loop(
_CharT __ct,
int __base,
char* __a,
char*& __a_end,
unsigned& __dc,
_CharT __thousands_sep,
const string& __grouping,
unsigned* __g,
unsigned*& __g_end,
_CharT* __atoms) {
if (__a_end == __a && (__ct == __atoms[24] || __ct == __atoms[25])) {
*__a_end++ = __ct == __atoms[24] ? '+' : '-';
__dc = 0;
return 0;
}
if (__grouping.size() != 0 && __ct == __thousands_sep) {
if (__g_end - __g < __num_get_buf_sz) {
*__g_end++ = __dc;
__dc = 0;
}
return 0;
}
ptrdiff_t __f = std::find(__atoms, __atoms + __int_chr_cnt, __ct) - __atoms;
if (__f >= 24)
return -1;
switch (__base) {
case 8:
case 10:
if (__f >= __base)
return -1;
break;
case 16:
if (__f < 22)
break;
if (__a_end != __a && __a_end - __a <= 2 && __a_end[-1] == '0') {
__dc = 0;
*__a_end++ = __src[__f];
return 0;
}
return -1;
}
*__a_end++ = __src[__f];
++__dc;
return 0;
}
template <class _CharT>
int __num_get<_CharT>::__stage2_float_loop(
_CharT __ct,
@ -274,65 +241,6 @@ _LIBCPP_HIDE_FROM_ABI _Tp __num_get_float(const char* __a, const char* __a_end,
return 0;
}
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 _CharT, class _InputIterator = istreambuf_iterator<_CharT> >
class num_get : public locale::facet, private __num_get<_CharT> {
public:
@ -470,137 +378,194 @@ protected:
return __b;
}
template <class _Signed>
_LIBCPP_HIDE_FROM_ABI iter_type
__do_get_signed(iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Signed& __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 (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)
__err |= ios_base::eofbit;
return __b;
}
template <class _MaybeSigned>
iter_type __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>;
template <class _Unsigned>
_LIBCPP_HIDE_FROM_ABI iter_type
__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;
// Stages 2 & 3
// These are combined into a single step where we parse the characters and calculate the value in one go instead of
// storing the relevant characters first (in an allocated buffer) and parse the characters after we extracted them.
// This makes the whole process significantly faster, since we avoid potential allocations and copies.
const auto& __numpunct = use_facet<numpunct<_CharT> >(__iob.getloc());
char_type __thousands_sep = __numpunct.thousands_sep();
string __grouping = __numpunct.grouping();
char_type __atoms_buffer[__num_get_base::__int_chr_cnt];
const char_type* __atoms = this->__do_widen(__iob, __atoms_buffer);
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 (this->__stage2_int_loop(
*__b,
__base,
__a,
__a_end,
__dc,
__thousands_sep,
__grouping,
__g,
__g_end,
const_cast<char_type*>(__atoms)))
break;
if (__first == __last) {
__err |= ios_base::eofbit | ios_base::failbit;
__v = 0;
return __first;
}
while (!__grouping.empty() && *__first == __thousands_sep) {
++__first;
if (__g_end - __g < this->__num_get_buf_sz)
*__g_end++ = 0;
}
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 | ios_base::failbit;
__v = 0;
return __first;
}
bool __parsed_num = false;
// If we don't have a pre-set base, figure it out and swallow any prefix
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 the base has been specified explicitly, try to swallow the appropriate prefix. We only need to do something
// special for hex, since decimal has no prefix and octal's prefix is '0', which doesn't change the value that
// we'll parse if we don't swallow it.
} else if (__base == 16) {
// Try to swallow '0x'
// *__first == '0'
if (*__first == __atoms[0]) {
++__first;
if (__first == __last) {
__err |= ios_base::eofbit;
__v = 0;
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) // Not a valid integer character
break;
if (__base == 16 && __offset >= 16)
__offset -= 6;
if (__offset >= __base)
break;
// __val = (__val * __base) + __offset
__overflowed |= __builtin_mul_overflow(__val, __base, std::addressof(__val)) ||
__builtin_add_overflow(__val, __offset, std::addressof(__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;
}
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 {
@ -654,40 +619,13 @@ _InputIterator num_get<_CharT, _InputIterator>::do_get(
template <class _CharT, class _InputIterator>
_InputIterator num_get<_CharT, _InputIterator>::do_get(
iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, void*& __v) const {
// Stage 1
int __base = 16;
// Stage 2
char_type __atoms[__num_get_base::__int_chr_cnt];
char_type __thousands_sep = char_type();
string __grouping;
std::use_facet<ctype<_CharT> >(__iob.getloc())
.widen(__num_get_base::__src, __num_get_base::__src + __num_get_base::__int_chr_cnt, __atoms);
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 (this->__stage2_int_loop(*__b, __base, __a, __a_end, __dc, __thousands_sep, __grouping, __g, __g_end, __atoms))
break;
}
// Stage 3
__buf.resize(__a_end - __a);
if (__locale::__sscanf(__buf.c_str(), _LIBCPP_GET_C_LOCALE, "%p", &__v) != 1)
__err = ios_base::failbit;
// EOF checked
if (__b == __e)
__err |= ios_base::eofbit;
return __b;
auto __flags = __iob.flags();
__iob.flags((__flags & ~ios_base::basefield & ~ios_base::uppercase) | ios_base::hex);
uintptr_t __ptr;
auto __res = __do_get_integral(__b, __e, __iob, __err, __ptr);
__iob.flags(__flags);
__v = reinterpret_cast<void*>(__ptr);
return __res;
}
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS num_get<char>;

View File

@ -79,15 +79,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
//
@ -211,12 +202,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __
char** __s, __locale_t __loc, const char* __format, _Args&&... __args) {
return ::asprintf_l(__s, __loc, __format, std::forward<_Args>(__args)...); // non-standard
}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
const char* __s, __locale_t __loc, const char* __format, _Args&&... __args) {
return ::sscanf_l(__s, __loc, __format, std::forward<_Args>(__args)...);
}
_LIBCPP_DIAGNOSTIC_POP
#undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT

View File

@ -141,13 +141,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __
__locale_guard __current(__loc);
return ::asprintf(__s, __format, std::forward<_Args>(__args)...); // non-standard
}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
const char* __s, __locale_t __loc, const char* __format, _Args&&... __args) {
__locale_guard __current(__loc);
return std::sscanf(__s, __format, std::forward<_Args>(__args)...);
}
_LIBCPP_DIAGNOSTIC_POP
#undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT

View File

@ -94,25 +94,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) {
#if !_LIBCPP_HAS_MUSL_LIBC
return ::strtoll_l(__nptr, __endptr, __base, __loc);
#else
(void)__loc;
return ::strtoll(__nptr, __endptr, __base);
#endif
}
inline _LIBCPP_HIDE_FROM_ABI unsigned long long
__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
#if !_LIBCPP_HAS_MUSL_LIBC
return ::strtoull_l(__nptr, __endptr, __base, __loc);
#else
(void)__loc;
return ::strtoull(__nptr, __endptr, __base);
#endif
}
//
// Character manipulation functions
//
@ -257,20 +238,6 @@ inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __asprintf(
va_end(__va);
return __res;
}
#ifndef _LIBCPP_COMPILER_GCC // GCC complains that this can't be always_inline due to C-style varargs
_LIBCPP_HIDE_FROM_ABI
#endif
inline _LIBCPP_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
const char* __s, __locale_t __loc, const char* __format, ...) {
va_list __va;
va_start(__va, __format);
__locale_guard __current(__loc);
int __res = std::vsscanf(__s, __format, __va);
va_end(__va);
return __res;
}
} // namespace __locale
_LIBCPP_END_NAMESPACE_STD

View File

@ -34,15 +34,6 @@ inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __
return std::strtold(__nptr, __endptr);
}
inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t) {
return std::strtoll(__nptr, __endptr, __base);
}
inline _LIBCPP_HIDE_FROM_ABI unsigned long long
__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t) {
return std::strtoull(__nptr, __endptr, __base);
}
} // namespace __locale
_LIBCPP_END_NAMESPACE_STD

View File

@ -186,14 +186,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
//
@ -276,23 +268,6 @@ _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 4, 5) int __snpri
_LIBCPP_EXPORTED_FROM_ABI
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __asprintf(char** __ret, __locale_t __loc, const char* __format, ...);
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wgcc-compat")
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") // GCC doesn't support [[gnu::format]] on variadic templates
#ifdef _LIBCPP_COMPILER_CLANG_BASED
# define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) _LIBCPP_ATTRIBUTE_FORMAT(__VA_ARGS__)
#else
# define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) /* nothing */
#endif
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
const char* __dest, __locale_t __loc, const char* __format, _Args&&... __args) {
return ::_sscanf_l(__dest, __format, __loc, std::forward<_Args>(__args)...);
}
_LIBCPP_DIAGNOSTIC_POP
#undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT
#if defined(_LIBCPP_BUILDING_LIBRARY)
struct __locale_guard {
_LIBCPP_HIDE_FROM_ABI __locale_guard(__locale_t __l) : __status(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)) {

View File

@ -34,12 +34,4 @@ inline _LIBCPP_HIDE_FROM_ABI long double strtold_l(const char* __nptr, char** __
return ::strtold(__nptr, __endptr);
}
inline _LIBCPP_HIDE_FROM_ABI long long strtoll_l(const char* __nptr, char** __endptr, int __base, locale_t) {
return ::strtoll(__nptr, __endptr, __base);
}
inline _LIBCPP_HIDE_FROM_ABI unsigned long long strtoull_l(const char* __nptr, char** __endptr, int __base, locale_t) {
return ::strtoull(__nptr, __endptr, __base);
}
#endif // _LIBCPP___SUPPORT_XLOCALE_STRTONUM_FALLBACK_H

View File

@ -1591,7 +1591,6 @@ module std [system] {
module locale_base_api {
textual header "__locale_dir/locale_base_api/bsd_locale_fallbacks.h"
textual header "__locale_dir/locale_base_api/ibm.h"
textual header "__locale_dir/locale_base_api/musl.h"
textual header "__locale_dir/locale_base_api/openbsd.h"
}
export *

View File

@ -5557,6 +5557,54 @@ string __num_get<_CharT>::__stage2_int_prep(ios_base& __iob, _CharT* __atoms, _C
return __np.grouping();
}
template <class _CharT>
int __num_get<_CharT>::__stage2_int_loop(
_CharT __ct,
int __base,
char* __a,
char*& __a_end,
unsigned& __dc,
_CharT __thousands_sep,
const string& __grouping,
unsigned* __g,
unsigned*& __g_end,
_CharT* __atoms) {
if (__a_end == __a && (__ct == __atoms[24] || __ct == __atoms[25])) {
*__a_end++ = __ct == __atoms[24] ? '+' : '-';
__dc = 0;
return 0;
}
if (__grouping.size() != 0 && __ct == __thousands_sep) {
if (__g_end - __g < __num_get_buf_sz) {
*__g_end++ = __dc;
__dc = 0;
}
return 0;
}
ptrdiff_t __f = __atoms_offset(__atoms, __ct);
if (__f >= 24)
return -1;
switch (__base) {
case 8:
case 10:
if (__f >= __base)
return -1;
break;
case 16:
if (__f < 22)
break;
if (__a_end != __a && __a_end - __a <= 2 && __a_end[-1] == '0') {
__dc = 0;
*__a_end++ = __src[__f];
return 0;
}
return -1;
}
*__a_end++ = __src[__f];
++__dc;
return 0;
}
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS collate<char>;
_LIBCPP_IF_WIDE_CHARACTERS(template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS collate<wchar_t>;)

View File

@ -13,6 +13,8 @@
// iter_type get(iter_type in, iter_type end, ios_base&,
// ios_base::iostate& err, long& v) const;
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
#include <locale>
#include <ios>
#include <cassert>
@ -98,6 +100,18 @@ int main(int, char**)
assert(err == ios.goodbit);
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[] = "0x123";
std::hex(ios);
@ -519,6 +533,142 @@ int main(int, char**)
assert(err == ios.failbit);
assert(v == std::numeric_limits<long>::max());
}
{
v = -1;
const char str[] = "";
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), ios, err, v);
assert(base(iter) == str);
assert(err == (std::ios::eofbit | std::ios::failbit));
assert(v == 0);
}
{
v = -1;
const char str[] = "+";
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 + 1), ios, err, v);
assert(base(iter) == str + 1);
assert(err == (std::ios::eofbit | std::ios::failbit));
assert(v == 0);
}
{
v = -1;
const char str[] = "+";
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(std::begin(str)),
cpp17_input_iterator<const char*>(std::end(str)),
ios,
err,
v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0);
}
{
v = -1;
const char str[] = "-";
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(std::begin(str)),
cpp17_input_iterator<const char*>(std::end(str)),
ios,
err,
v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0);
}
{
v = -1;
const char str[] = "0";
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(std::begin(str)),
cpp17_input_iterator<const char*>(std::end(str)),
ios,
err,
v);
assert(base(iter) == str + 1);
assert(err == ios.goodbit);
assert(v == 0);
}
{
v = -1;
const char str[] = "078";
std::ios_base::iostate err = ios.goodbit;
ios.flags(ios.flags() & ~ios.basefield);
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(std::begin(str)),
cpp17_input_iterator<const char*>(std::end(str)),
ios,
err,
v);
assert(base(iter) == str + 2);
assert(err == ios.goodbit);
assert(v == 7);
ios.flags(ios.flags() | ios.dec);
}
{
v = -1;
std::string str = std::to_string(std::numeric_limits<unsigned long>::max()) + "99a";
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str.data()),
cpp17_input_iterator<const char*>(str.data() + str.size()),
ios,
err,
v);
assert(base(iter) == str.data() + str.size() - 1);
assert(err == ios.failbit);
assert(v == std::numeric_limits<long>::max());
}
{
std::string str = std::to_string(std::numeric_limits<long>::max()) + 'c';
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter =
f.get(cpp17_input_iterator<const char*>(str.data()),
cpp17_input_iterator<const char*>(str.data() + str.size()),
ios, err, v);
assert(base(iter) == str.data() + str.size() - 1);
assert(err == ios.goodbit);
assert(v == std::numeric_limits<long>::max());
}
{
std::string str = std::to_string(static_cast<unsigned long>(std::numeric_limits<long>::max()) + 1) + 'c';
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str.data()),
cpp17_input_iterator<const char*>(str.data() + str.size()),
ios,
err,
v);
assert(base(iter) == str.data() + str.size() - 1);
assert(err == ios.failbit);
assert(v == std::numeric_limits<long>::max());
}
{
std::string str = '-' + std::to_string(static_cast<unsigned long>(std::numeric_limits<long>::max()) + 2) + 'c';
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str.data()),
cpp17_input_iterator<const char*>(str.data() + str.size()),
ios,
err,
v);
assert(base(iter) == str.data() + str.size() - 1);
assert(err == ios.failbit);
assert(v == std::numeric_limits<long>::min());
}
return 0;
}

View File

@ -68,6 +68,17 @@ int main(int, char**)
assert(err == ios.goodbit);
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());
}
std::hex(ios);
{
const char str[] = "0xFFFFFFFF";

View File

@ -68,6 +68,17 @@ int main(int, char**)
assert(err == ios.goodbit);
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());
}
std::hex(ios);
{
const char str[] = "0xFFFFFFFF";

View File

@ -68,6 +68,17 @@ int main(int, char**)
assert(err == ios.goodbit);
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());
}
std::hex(ios);
{
const char str[] = "0xFFFFFFFFFFFFFFFF";

View File

@ -68,6 +68,17 @@ int main(int, char**)
assert(err == ios.goodbit);
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());
}
std::hex(ios);
{
const char str[] = "0xFFFF";