tetl 0.1.0
Embedded Template Library
Loading...
Searching...
No Matches
to_integer.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSL-1.0
2
3#ifndef TETL_STRINGS_TO_INTEGER_HPP
4#define TETL_STRINGS_TO_INTEGER_HPP
5
15#include <etl/_numeric/abs.hpp>
18
19namespace etl::strings {
20
21namespace detail {
22
23template <integral Int>
24struct nop_overflow_checker {
25 explicit constexpr nop_overflow_checker(Int /*base*/) noexcept { }
26
27 [[nodiscard]] constexpr auto operator()(Int /*value*/, Int /*digit*/) const noexcept -> bool { return false; }
28};
29
30template <integral Int>
31struct unsigned_overflow_checker {
32 explicit constexpr unsigned_overflow_checker(Int base) noexcept
33 : _base{base}
34 {
35 }
36
37 [[nodiscard]] constexpr auto operator()(Int value, Int digit) const noexcept -> bool
38 {
39 return value > _maxDivBase or (value == _maxDivBase and digit > _maxModBase);
40 }
41
42private:
43 Int _base;
44 Int _maxDivBase{static_cast<Int>(numeric_limits<Int>::max() / _base)};
45 Int _maxModBase{static_cast<Int>(numeric_limits<Int>::max() % _base)};
46};
47
48template <integral Int>
49struct signed_overflow_checker {
50 explicit constexpr signed_overflow_checker(Int base) noexcept
51 : _base{base}
52 {
53 }
54
55 [[nodiscard]] constexpr auto operator()(Int value, Int digit) const noexcept -> bool
56 {
57 return value < _minDivBase or (value == _minDivBase and digit > _minModBase);
58 }
59
60private:
61 Int _base;
62 Int _minDivBase{static_cast<Int>(numeric_limits<Int>::min() / _base)};
63 Int _minModBase{abs(static_cast<Int>(numeric_limits<Int>::min() % _base))};
64};
65
66template <integral Int, bool Check>
67using overflow_checker = conditional_t<
68 Check,
69 conditional_t<signed_integral<Int>, signed_overflow_checker<Int>, unsigned_overflow_checker<Int>>,
70 nop_overflow_checker<Int>>;
71
72} // namespace detail
73
75 bool skip_whitespace = true;
76 bool check_overflow = true;
77};
78
79enum struct to_integer_error : unsigned char {
83};
84
85template <integral Int>
91
92template <integral Int, to_integer_options Options = to_integer_options{}>
93[[nodiscard]] constexpr auto to_integer(string_view str, Int base = Int(10)) noexcept -> to_integer_result<Int>
94{
95 auto const length = str.size();
96 auto const wouldOverflow = detail::overflow_checker<Int, Options.check_overflow>{base};
97 auto const makeError = [str](auto err) { return to_integer_result<Int>{.end = str.data(), .error = err}; };
98 auto const parseDigit = [](int ch) -> Int {
99 if (etl::isdigit(ch) != 0) {
100 return static_cast<Int>(ch - int{'0'});
101 }
102 if (etl::isalpha(ch) != 0) {
103 return static_cast<Int>(static_cast<Int>(etl::tolower(ch)) - Int{'a'} + Int{10});
104 }
105 return etl::numeric_limits<Int>::max(); // always greater than base
106 };
107
108 auto pos = size_t{};
109 if constexpr (Options.skip_whitespace) {
110 while (pos != length and etl::isspace(static_cast<int>(str[pos]))) {
111 ++pos;
112 }
113 }
114
115 // empty or only whitespace
116 if (pos == length) {
117 return makeError(to_integer_error::invalid_input);
118 }
119
120 // optional minus for signed types
121 [[maybe_unused]] auto positive = true;
122 if constexpr (signed_integral<Int>) {
123 if (str[pos] == '-') {
124 positive = false;
125 if (++pos == length) {
126 // minus "-" was last character in string
127 return makeError(to_integer_error::invalid_input);
128 }
129 }
130 }
131
132 // first digit
133 auto value = [&] {
134 auto const ch = static_cast<int>(str[pos++]);
135 auto const digit = static_cast<Int>(parseDigit(ch));
136 if constexpr (signed_integral<Int>) {
137 return static_cast<Int>(-digit);
138 } else {
139 return digit;
140 }
141 }();
142
143 if (etl::abs(value) >= base) {
144 return makeError(to_integer_error::invalid_input);
145 }
146
147 // loop over rest of digits
148 for (; pos != length; ++pos) {
149 auto const digit = parseDigit(static_cast<int>(str[pos]));
150 if (digit >= base) {
151 break;
152 }
153
154 if (wouldOverflow(value, digit)) {
155 return makeError(to_integer_error::overflow);
156 }
157
158 if constexpr (signed_integral<Int>) {
159 value = static_cast<Int>(value * base - digit);
160 } else {
161 value = static_cast<Int>(value * base + digit);
162 }
163 }
164
165 if constexpr (signed_integral<Int>) {
166 if (positive) {
167 if (value == numeric_limits<Int>::min()) {
168 return makeError(to_integer_error::overflow);
169 }
170 value *= Int(-1);
171 }
172 }
173
174 auto const end = etl::next(str.data(), static_cast<etl::ptrdiff_t>(pos));
175 return {.end = end, .error = to_integer_error::none, .value = value};
176}
177
178} // namespace etl::strings
179
180#endif // TETL_STRINGS_TO_INTEGER_HPP
The concept integral<T> is satisfied if and only if T is an integral type.
Definition integral.hpp:13
The concept signed_integral<T> is satisfied if and only if T is an integral type and is_signed_v<T> i...
Definition signed_integral.hpp:15
constexpr auto isalpha(int ch) noexcept -> int
Checks if the given character is an alphabetic character as classified by the default C locale.
Definition isalpha.hpp:18
constexpr auto isdigit(int ch) noexcept -> int
Checks if the given character is one of the 10 decimal digits: 0123456789.
Definition isdigit.hpp:15
constexpr auto isspace(int ch) noexcept -> int
Checks if the given character is whitespace character as classified by the default C locale.
Definition isspace.hpp:19
constexpr auto tolower(int ch) noexcept -> int
Converts the given character to lowercase according to the character conversion rules defined by the ...
Definition tolower.hpp:26
constexpr auto abs(complex< T > const &z) -> T
Definition abs.hpp:13
constexpr auto end(C &c) -> decltype(c.end())
Returns an iterator to the end (i.e. the element after the last element) of the given container c or ...
Definition end.hpp:14
constexpr auto next(InputIt it, typename iterator_traits< InputIt >::difference_type n=1) -> InputIt
Return the nth successor of iterator it.
Definition next.hpp:14
basic_string_view< char, etl::char_traits< char > > string_view
Typedef for common character type char
Definition basic_string_view.hpp:704
Definition find.hpp:8
to_integer_error
Definition to_integer.hpp:79
@ overflow
Definition to_integer.hpp:82
@ invalid_input
Definition to_integer.hpp:81
@ none
Definition to_integer.hpp:80
@ overflow
Definition from_floating_point.hpp:20
@ none
Definition from_floating_point.hpp:19
constexpr auto to_integer(string_view str, Int base=Int(10)) noexcept -> to_integer_result< Int >
Definition to_integer.hpp:93
@ invalid_input
Definition to_floating_point.hpp:15
typename conditional< B, T, F >::type conditional_t
Definition conditional.hpp:21
TETL_BUILTIN_PTRDIFF ptrdiff_t
etl::ptrdiff_t is the signed integer type of the result of subtracting two pointers.
Definition ptrdiff_t.hpp:14
constexpr auto data() const noexcept -> const_pointer
Returns a pointer to the underlying character array. The pointer is such that the range [data(); data...
Definition basic_string_view.hpp:171
constexpr auto size() const noexcept -> size_type
Returns the number of Char elements in the view, i.e. etl::distance(begin(), end()).
Definition basic_string_view.hpp:174
static constexpr auto max() noexcept
Definition numeric_limits.hpp:21
static constexpr auto min() noexcept
Definition numeric_limits.hpp:20
Definition to_integer.hpp:74
bool check_overflow
Definition to_integer.hpp:76
bool skip_whitespace
Definition to_integer.hpp:75
Definition to_integer.hpp:86
to_integer_error error
Definition to_integer.hpp:88
Int value
Definition to_integer.hpp:89
char const * end
Definition to_integer.hpp:87