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