xref: /freebsd/contrib/llvm-project/libcxx/include/__charconv/from_chars_integral.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
11 #define _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
12 
13 #include <__algorithm/copy_n.h>
14 #include <__assert>
15 #include <__charconv/from_chars_result.h>
16 #include <__charconv/traits.h>
17 #include <__config>
18 #include <__memory/addressof.h>
19 #include <__system_error/errc.h>
20 #include <__type_traits/enable_if.h>
21 #include <__type_traits/integral_constant.h>
22 #include <__type_traits/is_integral.h>
23 #include <__type_traits/is_unsigned.h>
24 #include <__type_traits/make_unsigned.h>
25 #include <limits>
26 
27 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
28 #  pragma GCC system_header
29 #endif
30 
31 _LIBCPP_PUSH_MACROS
32 #include <__undef_macros>
33 
34 _LIBCPP_BEGIN_NAMESPACE_STD
35 
36 #if _LIBCPP_STD_VER >= 17
37 
38 from_chars_result from_chars(const char*, const char*, bool, int = 10) = delete;
39 
40 template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
41 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__sign_combinator(_It __first,_It __last,_Tp & __value,_Fn __f,_Ts...__args)42 __sign_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) {
43   using __tl = numeric_limits<_Tp>;
44   decltype(std::__to_unsigned_like(__value)) __x;
45 
46   bool __neg = (__first != __last && *__first == '-');
47   auto __r   = __f(__neg ? __first + 1 : __first, __last, __x, __args...);
48   switch (__r.ec) {
49   case errc::invalid_argument:
50     return {__first, __r.ec};
51   case errc::result_out_of_range:
52     return __r;
53   default:
54     break;
55   }
56 
57   if (__neg) {
58     if (__x <= std::__complement(std::__to_unsigned_like(__tl::min()))) {
59       __x = std::__complement(__x);
60       std::copy_n(std::addressof(__x), 1, std::addressof(__value));
61       return __r;
62     }
63   } else {
64     if (__x <= std::__to_unsigned_like(__tl::max())) {
65       __value = __x;
66       return __r;
67     }
68   }
69 
70   return {__r.ptr, errc::result_out_of_range};
71 }
72 
73 template <typename _Tp>
__in_pattern(_Tp __c)74 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool __in_pattern(_Tp __c) {
75   return '0' <= __c && __c <= '9';
76 }
77 
78 struct _LIBCPP_HIDDEN __in_pattern_result {
79   bool __ok;
80   int __val;
81 
82   explicit _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI operator bool() const { return __ok; }
83 };
84 
85 template <typename _Tp>
__in_pattern(_Tp __c,int __base)86 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI __in_pattern_result __in_pattern(_Tp __c, int __base) {
87   if (__base <= 10)
88     return {'0' <= __c && __c < '0' + __base, __c - '0'};
89   else if (std::__in_pattern(__c))
90     return {true, __c - '0'};
91   else if ('a' <= __c && __c < 'a' + __base - 10)
92     return {true, __c - 'a' + 10};
93   else
94     return {'A' <= __c && __c < 'A' + __base - 10, __c - 'A' + 10};
95 }
96 
97 template <typename _It, typename _Tp, typename _Fn, typename... _Ts>
98 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)99 __subject_seq_combinator(_It __first, _It __last, _Tp& __value, _Fn __f, _Ts... __args) {
100   auto __find_non_zero = [](_It __firstit, _It __lastit) {
101     for (; __firstit != __lastit; ++__firstit)
102       if (*__firstit != '0')
103         break;
104     return __firstit;
105   };
106 
107   auto __p = __find_non_zero(__first, __last);
108   if (__p == __last || !std::__in_pattern(*__p, __args...)) {
109     if (__p == __first)
110       return {__first, errc::invalid_argument};
111     else {
112       __value = 0;
113       return {__p, {}};
114     }
115   }
116 
117   auto __r = __f(__p, __last, __value, __args...);
118   if (__r.ec == errc::result_out_of_range) {
119     for (; __r.ptr != __last; ++__r.ptr) {
120       if (!std::__in_pattern(*__r.ptr, __args...))
121         break;
122     }
123   }
124 
125   return __r;
126 }
127 
128 template <typename _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
129 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_atoi(const char * __first,const char * __last,_Tp & __value)130 __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) {
131   using __tx          = __itoa::__traits<_Tp>;
132   using __output_type = typename __tx::type;
133 
134   return std::__subject_seq_combinator(
135       __first, __last, __value, [](const char* __f, const char* __l, _Tp& __val) -> from_chars_result {
136         __output_type __a, __b;
137         auto __p = __tx::__read(__f, __l, __a, __b);
138         if (__p == __l || !std::__in_pattern(*__p)) {
139           __output_type __m = numeric_limits<_Tp>::max();
140           if (__m >= __a && __m - __a >= __b) {
141             __val = __a + __b;
142             return {__p, {}};
143           }
144         }
145         return {__p, errc::result_out_of_range};
146       });
147 }
148 
149 template <typename _Tp, __enable_if_t<is_signed<_Tp>::value, int> = 0>
150 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
__from_chars_atoi(const char * __first,const char * __last,_Tp & __value)151 __from_chars_atoi(const char* __first, const char* __last, _Tp& __value) {
152   using __t = decltype(std::__to_unsigned_like(__value));
153   return std::__sign_combinator(__first, __last, __value, __from_chars_atoi<__t>);
154 }
155 
156 /*
157 // Code used to generate __from_chars_log2f_lut.
158 #include <cmath>
159 #include <format>
160 #include <iostream>
161 
162 int main() {
163   for (int i = 2; i <= 36; ++i)
164     std::cout << std::format("{},\n", log2f(i));
165 }
166 */
167 /// log2f table for bases [2, 36].
168 inline constexpr float __from_chars_log2f_lut[35] = {
169     1,         1.5849625, 2,         2.321928, 2.5849626, 2.807355, 3,        3.169925,  3.321928,
170     3.4594316, 3.5849626, 3.7004397, 3.807355, 3.9068906, 4,        4.087463, 4.169925,  4.2479277,
171     4.321928,  4.3923173, 4.4594316, 4.523562, 4.5849624, 4.643856, 4.70044,  4.7548876, 4.807355,
172     4.857981,  4.9068904, 4.9541965, 5,        5.044394,  5.087463, 5.129283, 5.169925};
173 
174 template <typename _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
175 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)176 __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) {
177   if (__base == 10)
178     return std::__from_chars_atoi(__first, __last, __value);
179 
180   return std::__subject_seq_combinator(
181       __first,
182       __last,
183       __value,
184       [](const char* __p, const char* __lastp, _Tp& __val, int __b) -> from_chars_result {
185         using __tl = numeric_limits<_Tp>;
186         // __base is always between 2 and 36 inclusive.
187         auto __digits = __tl::digits / __from_chars_log2f_lut[__b - 2];
188         _Tp __x = __in_pattern(*__p++, __b).__val, __y = 0;
189 
190         for (int __i = 1; __p != __lastp; ++__i, ++__p) {
191           if (auto __c = __in_pattern(*__p, __b)) {
192             if (__i < __digits - 1)
193               __x = __x * __b + __c.__val;
194             else {
195               if (!__itoa::__mul_overflowed(__x, __b, __x))
196                 ++__p;
197               __y = __c.__val;
198               break;
199             }
200           } else
201             break;
202         }
203 
204         if (__p == __lastp || !__in_pattern(*__p, __b)) {
205           if (__tl::max() - __x >= __y) {
206             __val = __x + __y;
207             return {__p, {}};
208           }
209         }
210         return {__p, errc::result_out_of_range};
211       },
212       __base);
213 }
214 
215 template <typename _Tp, __enable_if_t<is_signed<_Tp>::value, int> = 0>
216 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)217 __from_chars_integral(const char* __first, const char* __last, _Tp& __value, int __base) {
218   using __t = decltype(std::__to_unsigned_like(__value));
219   return std::__sign_combinator(__first, __last, __value, __from_chars_integral<__t>, __base);
220 }
221 
222 template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
223 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
from_chars(const char * __first,const char * __last,_Tp & __value)224 from_chars(const char* __first, const char* __last, _Tp& __value) {
225   return std::__from_chars_atoi(__first, __last, __value);
226 }
227 
228 template <typename _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
229 inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI from_chars_result
from_chars(const char * __first,const char * __last,_Tp & __value,int __base)230 from_chars(const char* __first, const char* __last, _Tp& __value, int __base) {
231   _LIBCPP_ASSERT_UNCATEGORIZED(2 <= __base && __base <= 36, "base not in [2, 36]");
232   return std::__from_chars_integral(__first, __last, __value, __base);
233 }
234 #endif // _LIBCPP_STD_VER >= 17
235 
236 _LIBCPP_END_NAMESPACE_STD
237 
238 _LIBCPP_POP_MACROS
239 
240 #endif // _LIBCPP___CHARCONV_FROM_CHARS_INTEGRAL_H
241