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