xref: /freebsd/contrib/llvm-project/libcxx/include/__charconv/from_chars_integral.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric // -*- C++ -*-
206c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
306c3fb27SDimitry Andric //
406c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
506c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
606c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
706c3fb27SDimitry Andric //
806c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
906c3fb27SDimitry Andric 
1006c3fb27SDimitry Andric #ifndef _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
1106c3fb27SDimitry Andric #define _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
1206c3fb27SDimitry Andric 
1306c3fb27SDimitry Andric #include <__algorithm/copy_n.h>
14*0fca6ea1SDimitry Andric #include <__assert>
1506c3fb27SDimitry Andric #include <__charconv/from_chars_result.h>
1606c3fb27SDimitry Andric #include <__charconv/traits.h>
1706c3fb27SDimitry Andric #include <__config>
1806c3fb27SDimitry Andric #include <__memory/addressof.h>
1906c3fb27SDimitry Andric #include <__system_error/errc.h>
2006c3fb27SDimitry Andric #include <__type_traits/enable_if.h>
2106c3fb27SDimitry Andric #include <__type_traits/integral_constant.h>
2206c3fb27SDimitry Andric #include <__type_traits/is_integral.h>
2306c3fb27SDimitry Andric #include <__type_traits/is_unsigned.h>
2406c3fb27SDimitry Andric #include <__type_traits/make_unsigned.h>
2506c3fb27SDimitry Andric #include <limits>
2606c3fb27SDimitry Andric 
2706c3fb27SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2806c3fb27SDimitry Andric #  pragma GCC system_header
2906c3fb27SDimitry Andric #endif
3006c3fb27SDimitry Andric 
3106c3fb27SDimitry Andric _LIBCPP_PUSH_MACROS
3206c3fb27SDimitry Andric #include <__undef_macros>
3306c3fb27SDimitry Andric 
3406c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
3506c3fb27SDimitry Andric 
3606c3fb27SDimitry Andric #if _LIBCPP_STD_VER >= 17
3706c3fb27SDimitry Andric 
3806c3fb27SDimitry Andric from_chars_result from_chars(const char*, const char*, bool, int = 10) = delete;
3906c3fb27SDimitry Andric 
4006c3fb27SDimitry Andric template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
4106c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__sign_combinator(_It __first,_It __last,_Tp & __value,_Fn __f,_Ts...__args)4206c3fb27SDimitry Andric __sign_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) {
4306c3fb27SDimitry Andric   using __tl = numeric_limits<_Tp>;
4406c3fb27SDimitry Andric   decltype(std::__to_unsigned_like(__value)) __x;
4506c3fb27SDimitry Andric 
4606c3fb27SDimitry Andric   bool __neg = (__first != __last && *__first == '-');
4706c3fb27SDimitry Andric   auto __r   = __f(__neg ? __first + 1 : __first, __last, __x, __args...);
4806c3fb27SDimitry Andric   switch (__r.ec) {
4906c3fb27SDimitry Andric   case errc::invalid_argument:
5006c3fb27SDimitry Andric     return {__first, __r.ec};
5106c3fb27SDimitry Andric   case errc::result_out_of_range:
5206c3fb27SDimitry Andric     return __r;
5306c3fb27SDimitry Andric   default:
5406c3fb27SDimitry Andric     break;
5506c3fb27SDimitry Andric   }
5606c3fb27SDimitry Andric 
5706c3fb27SDimitry Andric   if (__neg) {
5806c3fb27SDimitry Andric     if (__x <= std::__complement(std::__to_unsigned_like(__tl::min()))) {
5906c3fb27SDimitry Andric       __x = std::__complement(__x);
6006c3fb27SDimitry Andric       std::copy_n(std::addressof(__x), 1, std::addressof(__value));
6106c3fb27SDimitry Andric       return __r;
6206c3fb27SDimitry Andric     }
6306c3fb27SDimitry Andric   } else {
6406c3fb27SDimitry Andric     if (__x <= std::__to_unsigned_like(__tl::max())) {
6506c3fb27SDimitry Andric       __value = __x;
6606c3fb27SDimitry Andric       return __r;
6706c3fb27SDimitry Andric     }
6806c3fb27SDimitry Andric   }
6906c3fb27SDimitry Andric 
7006c3fb27SDimitry Andric   return {__r.ptr, errc::result_out_of_range};
7106c3fb27SDimitry Andric }
7206c3fb27SDimitry Andric 
7306c3fb27SDimitry Andric template <typename _Tp>
__in_pattern(_Tp __c)7406c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __in_pattern(_Tp __c) {
7506c3fb27SDimitry Andric   return '0' <= __c && __c <= '9';
7606c3fb27SDimitry Andric }
7706c3fb27SDimitry Andric 
7806c3fb27SDimitry Andric struct _LIBCPP_HIDDEN __in_pattern_result {
7906c3fb27SDimitry Andric   bool __ok;
8006c3fb27SDimitry Andric   int __val;
8106c3fb27SDimitry Andric 
8206c3fb27SDimitry Andric   explicit _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI operator bool() const { return __ok; }
8306c3fb27SDimitry Andric };
8406c3fb27SDimitry Andric 
8506c3fb27SDimitry Andric template <typename _Tp>
__in_pattern(_Tp __c,int __base)8606c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __in_pattern_result __in_pattern(_Tp __c, int __base) {
8706c3fb27SDimitry Andric   if (__base <= 10)
8806c3fb27SDimitry Andric     return {'0' <= __c && __c < '0' + __base, __c - '0'};
8906c3fb27SDimitry Andric   else if (std::__in_pattern(__c))
9006c3fb27SDimitry Andric     return {true, __c - '0'};
9106c3fb27SDimitry Andric   else if ('a' <= __c && __c < 'a' + __base - 10)
9206c3fb27SDimitry Andric     return {true, __c - 'a' + 10};
9306c3fb27SDimitry Andric   else
9406c3fb27SDimitry Andric     return {'A' <= __c && __c < 'A' + __base - 10, __c - 'A' + 10};
9506c3fb27SDimitry Andric }
9606c3fb27SDimitry Andric 
9706c3fb27SDimitry Andric template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
9806c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__subject_seq_combinator(_It __first,_It __last,_Tp & __value,_Fn __f,_Ts...__args)9906c3fb27SDimitry Andric __subject_seq_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) {
10006c3fb27SDimitry Andric   auto __find_non_zero = [](_It __firstit, _It __lastit) {
10106c3fb27SDimitry Andric     for (; __firstit != __lastit; ++__firstit)
10206c3fb27SDimitry Andric       if (*__firstit != '0')
10306c3fb27SDimitry Andric         break;
10406c3fb27SDimitry Andric     return __firstit;
10506c3fb27SDimitry Andric   };
10606c3fb27SDimitry Andric 
10706c3fb27SDimitry Andric   auto __p = __find_non_zero(__first, __last);
10806c3fb27SDimitry Andric   if (__p == __last || !std::__in_pattern(*__p, __args...)) {
10906c3fb27SDimitry Andric     if (__p == __first)
11006c3fb27SDimitry Andric       return {__first, errc::invalid_argument};
11106c3fb27SDimitry Andric     else {
11206c3fb27SDimitry Andric       __value = 0;
11306c3fb27SDimitry Andric       return {__p, {}};
11406c3fb27SDimitry Andric     }
11506c3fb27SDimitry Andric   }
11606c3fb27SDimitry Andric 
11706c3fb27SDimitry Andric   auto __r = __f(__p, __last, __value, __args...);
11806c3fb27SDimitry Andric   if (__r.ec == errc::result_out_of_range) {
11906c3fb27SDimitry Andric     for (; __r.ptr != __last; ++__r.ptr) {
12006c3fb27SDimitry Andric       if (!std::__in_pattern(*__r.ptr, __args...))
12106c3fb27SDimitry Andric         break;
12206c3fb27SDimitry Andric     }
12306c3fb27SDimitry Andric   }
12406c3fb27SDimitry Andric 
12506c3fb27SDimitry Andric   return __r;
12606c3fb27SDimitry Andric }
12706c3fb27SDimitry Andric 
1285f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
12906c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_atoi(const char * __first,const char * __last,_Tp & __value)13006c3fb27SDimitry Andric __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) {
13106c3fb27SDimitry Andric   using __tx          = __itoa::__traits<_Tp>;
13206c3fb27SDimitry Andric   using __output_type = typename __tx::type;
13306c3fb27SDimitry Andric 
13406c3fb27SDimitry Andric   return std::__subject_seq_combinator(
13506c3fb27SDimitry Andric       __first, __last, __value, [](const char* __f, const char* __l, _Tp& __val) -> from_chars_result {
13606c3fb27SDimitry Andric         __output_type __a, __b;
13706c3fb27SDimitry Andric         auto __p = __tx::__read(__f, __l, __a, __b);
13806c3fb27SDimitry Andric         if (__p == __l || !std::__in_pattern(*__p)) {
13906c3fb27SDimitry Andric           __output_type __m = numeric_limits<_Tp>::max();
14006c3fb27SDimitry Andric           if (__m >= __a && __m - __a >= __b) {
14106c3fb27SDimitry Andric             __val = __a + __b;
14206c3fb27SDimitry Andric             return {__p, {}};
14306c3fb27SDimitry Andric           }
14406c3fb27SDimitry Andric         }
14506c3fb27SDimitry Andric         return {__p, errc::result_out_of_range};
14606c3fb27SDimitry Andric       });
14706c3fb27SDimitry Andric }
14806c3fb27SDimitry Andric 
1495f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_signed<_Tp>::value, int> = 0>
15006c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_atoi(const char * __first,const char * __last,_Tp & __value)15106c3fb27SDimitry Andric __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) {
15206c3fb27SDimitry Andric   using __t = decltype(std::__to_unsigned_like(__value));
15306c3fb27SDimitry Andric   return std::__sign_combinator(__first, __last, __value, __from_chars_atoi<__t>);
15406c3fb27SDimitry Andric }
15506c3fb27SDimitry Andric 
15606c3fb27SDimitry Andric /*
15706c3fb27SDimitry Andric // Code used to generate __from_chars_log2f_lut.
15806c3fb27SDimitry Andric #include <cmath>
15906c3fb27SDimitry Andric #include <format>
16006c3fb27SDimitry Andric #include <iostream>
16106c3fb27SDimitry Andric 
16206c3fb27SDimitry Andric int main() {
16306c3fb27SDimitry Andric   for (int i = 2; i <= 36; ++i)
16406c3fb27SDimitry Andric     std::cout << std::format("{},\n", log2f(i));
16506c3fb27SDimitry Andric }
16606c3fb27SDimitry Andric */
16706c3fb27SDimitry Andric /// log2f table for bases [2, 36].
16806c3fb27SDimitry Andric inline constexpr float __from_chars_log2f_lut[35] = {
16906c3fb27SDimitry Andric     1,         1.5849625, 2,         2.321928, 2.5849626, 2.807355, 3,        3.169925,  3.321928,
17006c3fb27SDimitry Andric     3.4594316, 3.5849626, 3.7004397, 3.807355, 3.9068906, 4,        4.087463, 4.169925,  4.2479277,
17106c3fb27SDimitry Andric     4.321928,  4.3923173, 4.4594316, 4.523562, 4.5849624, 4.643856, 4.70044,  4.7548876, 4.807355,
17206c3fb27SDimitry Andric     4.857981,  4.9068904, 4.9541965, 5,        5.044394,  5.087463, 5.129283, 5.169925};
17306c3fb27SDimitry Andric 
1745f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
17506c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_integral(const char * __first,const char * __last,_Tp & __value,int __base)17606c3fb27SDimitry Andric __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) {
17706c3fb27SDimitry Andric   if (__base == 10)
17806c3fb27SDimitry Andric     return std::__from_chars_atoi(__first, __last, __value);
17906c3fb27SDimitry Andric 
18006c3fb27SDimitry Andric   return std::__subject_seq_combinator(
18106c3fb27SDimitry Andric       __first,
18206c3fb27SDimitry Andric       __last,
18306c3fb27SDimitry Andric       __value,
18406c3fb27SDimitry Andric       [](const char* __p, const char* __lastp, _Tp& __val, int __b) -> from_chars_result {
18506c3fb27SDimitry Andric         using __tl = numeric_limits<_Tp>;
18606c3fb27SDimitry Andric         // __base is always between 2 and 36 inclusive.
18706c3fb27SDimitry Andric         auto __digits = __tl::digits / __from_chars_log2f_lut[__b - 2];
18806c3fb27SDimitry Andric         _Tp __x = __in_pattern(*__p++, __b).__val, __y = 0;
18906c3fb27SDimitry Andric 
19006c3fb27SDimitry Andric         for (int __i = 1; __p != __lastp; ++__i, ++__p) {
19106c3fb27SDimitry Andric           if (auto __c = __in_pattern(*__p, __b)) {
19206c3fb27SDimitry Andric             if (__i < __digits - 1)
19306c3fb27SDimitry Andric               __x = __x * __b + __c.__val;
19406c3fb27SDimitry Andric             else {
19506c3fb27SDimitry Andric               if (!__itoa::__mul_overflowed(__x, __b, __x))
19606c3fb27SDimitry Andric                 ++__p;
19706c3fb27SDimitry Andric               __y = __c.__val;
19806c3fb27SDimitry Andric               break;
19906c3fb27SDimitry Andric             }
20006c3fb27SDimitry Andric           } else
20106c3fb27SDimitry Andric             break;
20206c3fb27SDimitry Andric         }
20306c3fb27SDimitry Andric 
20406c3fb27SDimitry Andric         if (__p == __lastp || !__in_pattern(*__p, __b)) {
20506c3fb27SDimitry Andric           if (__tl::max() - __x >= __y) {
20606c3fb27SDimitry Andric             __val = __x + __y;
20706c3fb27SDimitry Andric             return {__p, {}};
20806c3fb27SDimitry Andric           }
20906c3fb27SDimitry Andric         }
21006c3fb27SDimitry Andric         return {__p, errc::result_out_of_range};
21106c3fb27SDimitry Andric       },
21206c3fb27SDimitry Andric       __base);
21306c3fb27SDimitry Andric }
21406c3fb27SDimitry Andric 
2155f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_signed<_Tp>::value, int> = 0>
21606c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_integral(const char * __first,const char * __last,_Tp & __value,int __base)21706c3fb27SDimitry Andric __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) {
21806c3fb27SDimitry Andric   using __t = decltype(std::__to_unsigned_like(__value));
21906c3fb27SDimitry Andric   return std::__sign_combinator(__first, __last, __value, __from_chars_integral<__t>, __base);
22006c3fb27SDimitry Andric }
22106c3fb27SDimitry Andric 
2225f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
22306c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
from_chars(const char * __first,const char * __last,_Tp & __value)22406c3fb27SDimitry Andric from_chars(const char* __first, const char* __last, _Tp& __value) {
22506c3fb27SDimitry Andric   return std::__from_chars_atoi(__first, __last, __value);
22606c3fb27SDimitry Andric }
22706c3fb27SDimitry Andric 
2285f757f3fSDimitry Andric template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
22906c3fb27SDimitry Andric inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
from_chars(const char * __first,const char * __last,_Tp & __value,int __base)23006c3fb27SDimitry Andric from_chars(const char* __first, const char* __last, _Tp& __value, int __base) {
23106c3fb27SDimitry Andric   _LIBCPP_ASSERT_UNCATEGORIZED(2 <= __base && __base <= 36, "base not in [2, 36]");
23206c3fb27SDimitry Andric   return std::__from_chars_integral(__first, __last, __value, __base);
23306c3fb27SDimitry Andric }
23406c3fb27SDimitry Andric #endif // _LIBCPP_STD_VER >= 17
23506c3fb27SDimitry Andric 
23606c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_STD
23706c3fb27SDimitry Andric 
23806c3fb27SDimitry Andric _LIBCPP_POP_MACROS
23906c3fb27SDimitry Andric 
24006c3fb27SDimitry Andric #endif // _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
241