1// Copyright 2023 Matt Borland
2// Distributed under the Boost Software License, Version 1.0.
3// https://www.boost.org/LICENSE_1_0.txt
4
5#ifndef BOOST_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
6#define BOOST_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
7
8#include <boost/charconv/detail/apply_sign.hpp>
9#include <boost/charconv/detail/config.hpp>
10#include <boost/charconv/detail/from_chars_result.hpp>
11#include <boost/charconv/detail/emulated128.hpp>
12#include <boost/charconv/detail/type_traits.hpp>
13#include <boost/charconv/config.hpp>
14#include <boost/config.hpp>
15#include <system_error>
16#include <type_traits>
17#include <limits>
18#include <cstdlib>
19#include <cerrno>
20#include <cstddef>
21#include <cstdint>
22
23namespace boost { namespace charconv { namespace detail {
24
25static constexpr unsigned char uchar_values[] =
26 {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
27 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
28 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
29 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
30 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
31 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
32 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
33 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
34 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
35 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
36 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
37 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
38 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
39 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
40 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
41 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
42
43static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char");
44
45// Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
46constexpr unsigned char digit_from_char(char val) noexcept
47{
48 return uchar_values[static_cast<unsigned char>(val)];
49}
50
51#ifdef BOOST_MSVC
52# pragma warning(push)
53# pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
54# pragma warning(disable: 4189) // 'is_negative': local variable is initialized but not referenced
55
56#elif defined(__clang__)
57# pragma clang diagnostic push
58# pragma clang diagnostic ignored "-Wconstant-conversion"
59
60#elif defined(__GNUC__) && (__GNUC__ < 7)
61# pragma GCC diagnostic push
62# pragma GCC diagnostic ignored "-Woverflow"
63# pragma GCC diagnostic ignored "-Wconversion"
64# pragma GCC diagnostic ignored "-Wsign-conversion"
65
66#elif defined(__GNUC__) && (__GNUC__ >= 7)
67# pragma GCC diagnostic push
68# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
69# pragma GCC diagnostic ignored "-Wconversion"
70
71#endif
72
73template <typename Integer, typename Unsigned_Integer>
74BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
75{
76 Unsigned_Integer result = 0;
77 Unsigned_Integer overflow_value = 0;
78 Unsigned_Integer max_digit = 0;
79
80 // Check pre-conditions
81 if (!((first <= last) && (base >= 2 && base <= 36)))
82 {
83 return {.ptr: first, .ec: std::errc::invalid_argument};
84 }
85
86 const auto unsigned_base = static_cast<Unsigned_Integer>(base);
87
88 // Strip sign if the type is signed
89 // Negative sign will be appended at the end of parsing
90 BOOST_ATTRIBUTE_UNUSED bool is_negative = false;
91 auto next = first;
92
93 BOOST_CHARCONV_IF_CONSTEXPR (is_signed<Integer>::value)
94 {
95 if (next != last)
96 {
97 if (*next == '-')
98 {
99 is_negative = true;
100 ++next;
101 }
102 else if (*next == '+' || *next == ' ')
103 {
104 return {.ptr: next, .ec: std::errc::invalid_argument};
105 }
106 }
107
108 #ifdef BOOST_CHARCONV_HAS_INT128
109 BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
110 {
111 overflow_value = BOOST_CHARCONV_INT128_MAX;
112 max_digit = BOOST_CHARCONV_INT128_MAX;
113 }
114 else
115 #endif
116 {
117 overflow_value = (std::numeric_limits<Integer>::max)();
118 max_digit = (std::numeric_limits<Integer>::max)();
119 }
120
121 if (is_negative)
122 {
123 ++overflow_value;
124 ++max_digit;
125 }
126 }
127 else
128 {
129 if (next != last && (*next == '-' || *next == '+' || *next == ' '))
130 {
131 return {.ptr: first, .ec: std::errc::invalid_argument};
132 }
133
134 #ifdef BOOST_CHARCONV_HAS_INT128
135 BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::uint128_type>::value)
136 {
137 overflow_value = BOOST_CHARCONV_UINT128_MAX;
138 max_digit = BOOST_CHARCONV_UINT128_MAX;
139 }
140 else
141 #endif
142 {
143 overflow_value = (std::numeric_limits<Unsigned_Integer>::max)();
144 max_digit = (std::numeric_limits<Unsigned_Integer>::max)();
145 }
146 }
147
148 #ifdef BOOST_CHARCONV_HAS_INT128
149 BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
150 {
151 overflow_value /= unsigned_base;
152 max_digit %= unsigned_base;
153 #ifndef __GLIBCXX_TYPE_INT_N_0
154 if (base != 10)
155 {
156 // Overflow value would cause INT128_MIN in non-base10 to fail
157 overflow_value *= static_cast<Unsigned_Integer>(2);
158 }
159 #endif
160 }
161 else
162 #endif
163 {
164 overflow_value /= unsigned_base;
165 max_digit %= unsigned_base;
166 }
167
168 // If the only character was a sign abort now
169 if (next == last)
170 {
171 return {.ptr: first, .ec: std::errc::invalid_argument};
172 }
173
174 bool overflowed = false;
175
176 const std::ptrdiff_t nc = last - next;
177
178 // In non-GNU mode on GCC numeric limits may not be specialized
179 #if defined(BOOST_CHARCONV_HAS_INT128) && !defined(__GLIBCXX_TYPE_INT_N_0)
180 constexpr std::ptrdiff_t nd = std::is_same<Integer, boost::int128_type>::value ? 38 :
181 std::is_same<Integer, boost::uint128_type>::value ? 38 :
182 std::numeric_limits<Integer>::digits10;
183 #else
184 constexpr std::ptrdiff_t nd = std::numeric_limits<Integer>::digits10;
185 #endif
186
187 {
188 std::ptrdiff_t i = 0;
189
190 for( ; i < nd && i < nc; ++i )
191 {
192 // overflow is not possible in the first nd characters
193
194 const unsigned char current_digit = digit_from_char(val: *next);
195
196 if (current_digit >= unsigned_base)
197 {
198 break;
199 }
200
201 result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
202 ++next;
203 }
204
205 for( ; i < nc; ++i )
206 {
207 const unsigned char current_digit = digit_from_char(val: *next);
208
209 if (current_digit >= unsigned_base)
210 {
211 break;
212 }
213
214 if (result < overflow_value || (result == overflow_value && current_digit <= max_digit))
215 {
216 result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
217 }
218 else
219 {
220 // Required to keep updating the value of next, but the result is garbage
221 overflowed = true;
222 }
223
224 ++next;
225 }
226 }
227
228 // Return the parsed value, adding the sign back if applicable
229 // If we have overflowed then we do not return the result
230 if (overflowed)
231 {
232 return {.ptr: next, .ec: std::errc::result_out_of_range};
233 }
234
235 value = static_cast<Integer>(result);
236
237 BOOST_IF_CONSTEXPR (is_signed<Integer>::value)
238 {
239 if (is_negative)
240 {
241 value = static_cast<Integer>(-(static_cast<Unsigned_Integer>(value)));
242 }
243 }
244
245 return {.ptr: next, .ec: std::errc()};
246}
247
248#ifdef BOOST_MSVC
249# pragma warning(pop)
250#elif defined(__clang__)
251# pragma clang diagnostic pop
252#elif defined(__GNUC__)
253# pragma GCC diagnostic pop
254#endif
255
256// Only from_chars for integer types is constexpr (as of C++23)
257template <typename Integer>
258BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
259{
260 using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
261 return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
262}
263
264#ifdef BOOST_CHARCONV_HAS_INT128
265template <typename Integer>
266BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first, const char* last, Integer& value, int base = 10) noexcept
267{
268 using Unsigned_Integer = boost::uint128_type;
269 return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
270}
271#endif
272
273BOOST_CHARCONV_GCC5_CONSTEXPR from_chars_result from_chars128(const char* first, const char* last, uint128& value, int base = 10) noexcept
274{
275 return from_chars_integer_impl<uint128, uint128>(first, last, value, base);
276}
277
278}}} // Namespaces
279
280#endif // BOOST_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
281

source code of boost/libs/charconv/include/boost/charconv/detail/from_chars_integer_impl.hpp