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// SPDX-FileCopyrightText: Copyright (C) 2024 Tobias Hienzsch
3
4#ifndef TETL_STRINGS_TO_INTEGER_HPP
5#define TETL_STRINGS_TO_INTEGER_HPP
6
7#include <etl/_cctype/isalpha.hpp>
8#include <etl/_cctype/isdigit.hpp>
9#include <etl/_cctype/isspace.hpp>
10#include <etl/_cctype/tolower.hpp>
11#include <etl/_concepts/integral.hpp>
12#include <etl/_concepts/signed_integral.hpp>
13#include <etl/_cstddef/size_t.hpp>
14#include <etl/_iterator/next.hpp>
15#include <etl/_limits/numeric_limits.hpp>
16#include <etl/_numeric/abs.hpp>
17#include <etl/_string_view/basic_string_view.hpp>
18#include <etl/_type_traits/conditional.hpp>
19
20namespace etl::strings {
21
22namespace detail {
23
24template <integral Int>
25struct nop_overflow_checker {
26 explicit constexpr nop_overflow_checker(Int /*base*/) noexcept { }
27
28 [[nodiscard]] constexpr auto operator()(Int /*value*/, Int /*digit*/) const noexcept -> bool
29 {
30 return false;
31 }
32};
33
34template <integral Int>
35struct unsigned_overflow_checker {
36 explicit constexpr unsigned_overflow_checker(Int base) noexcept
37 : _maxDivBase{static_cast<Int>(numeric_limits<Int>::max() / base)}
38 , _maxModBase{static_cast<Int>(numeric_limits<Int>::max() % base)}
39 {
40 }
41
42 [[nodiscard]] constexpr auto operator()(Int value, Int digit) const noexcept -> bool
43 {
44 return value > _maxDivBase or (value == _maxDivBase and digit > _maxModBase);
45 }
46
47private:
48 Int _maxDivBase;
49 Int _maxModBase;
50};
51
52template <integral Int>
53struct signed_overflow_checker {
54 explicit constexpr signed_overflow_checker(Int base) noexcept
55 : _minDivBase{static_cast<Int>(numeric_limits<Int>::min() / base)}
56 , _minModBase{abs(static_cast<Int>(numeric_limits<Int>::min() % base))}
57 {
58 }
59
60 [[nodiscard]] constexpr auto operator()(Int value, Int digit) const noexcept -> bool
61 {
62 return value < _minDivBase or (value == _minDivBase and digit > _minModBase);
63 }
64
65private:
66 Int _minDivBase;
67 Int _minModBase;
68};
69
70template <integral Int, bool Check>
71using overflow_checker = conditional_t<
72 Check,
73 conditional_t<signed_integral<Int>, signed_overflow_checker<Int>, unsigned_overflow_checker<Int>>,
74 nop_overflow_checker<Int>
75>;
76
77} // namespace detail
78
80 bool skip_whitespace = true;
81 bool check_overflow = true;
82};
83
84enum struct to_integer_error : unsigned char {
85 none,
88};
89
90template <integral Int>
92 char const* end{nullptr};
94 Int value{};
95};
96
97template <integral Int, to_integer_options Options = to_integer_options{}>
98[[nodiscard]] constexpr auto to_integer(string_view str, Int base = Int(10)) noexcept -> to_integer_result<Int>
99{
100 auto const length = str.size();
101 auto const wouldOverflow = detail::overflow_checker<Int, Options.check_overflow>{base};
102
103 auto const parseDigit = [](int ch) -> Int {
104 if (etl::isdigit(ch) != 0) {
105 return static_cast<Int>(ch - int{'0'});
106 }
107 if (etl::isalpha(ch) != 0) {
108 return static_cast<Int>(static_cast<Int>(etl::tolower(ch)) - Int{'a'} + Int{10});
109 }
110 return etl::numeric_limits<Int>::max(); // always greater than base
111 };
112
113 auto const invalidInputError = [end = str.data()] {
114 return to_integer_result<Int>{
115 .end = end,
117 };
118 };
119
120 auto const overflowError = [&]<bool MatchRestOfPattern>(auto pos, bool_constant<MatchRestOfPattern> /*bc*/) {
121 if constexpr (MatchRestOfPattern) {
122 for (; pos != length; ++pos) {
123 auto const digit = parseDigit(static_cast<int>(str[pos]));
124 if (digit >= base) {
125 break;
126 }
127 }
128 }
129 return to_integer_result<Int>{
130 .end = etl::next(str.data(), static_cast<etl::ptrdiff_t>(pos)),
132 };
133 };
134
135 auto pos = size_t{};
136 if constexpr (Options.skip_whitespace) {
137 while (pos != length and etl::isspace(static_cast<int>(str[pos]))) {
138 ++pos;
139 }
140 }
141
142 // empty or only whitespace
143 if (pos == length) {
144 return invalidInputError();
145 }
146
147 // optional minus for signed types
148 [[maybe_unused]] auto positive = true;
149 if constexpr (signed_integral<Int>) {
150 if (str[pos] == '-') {
151 positive = false;
152 if (++pos == length) {
153 // minus "-" was last character in string
154 return invalidInputError();
155 }
156 }
157 }
158
159 // first digit
160 auto value = [&] {
161 auto const ch = static_cast<int>(str[pos++]);
162 auto const digit = static_cast<Int>(parseDigit(ch));
163 if constexpr (signed_integral<Int>) {
164 return static_cast<Int>(-digit);
165 } else {
166 return digit;
167 }
168 }();
169
170 if (etl::abs(value) >= base) {
171 return invalidInputError();
172 }
173
174 // loop over rest of digits
175 for (; pos != length; ++pos) {
176 auto const digit = parseDigit(static_cast<int>(str[pos]));
177 if (digit >= base) {
178 break;
179 }
180
181 if (wouldOverflow(value, digit)) {
182 return overflowError(pos, etl::true_type{});
183 }
184
185 if constexpr (signed_integral<Int>) {
186 value = static_cast<Int>(value * base - digit);
187 } else {
188 value = static_cast<Int>(value * base + digit);
189 }
190 }
191
192 if constexpr (signed_integral<Int>) {
193 if (positive) {
194 if (value == numeric_limits<Int>::min()) {
195 return overflowError(pos, etl::false_type{});
196 }
197 value *= Int(-1);
198 }
199 }
200
201 auto const end = etl::next(str.data(), static_cast<etl::ptrdiff_t>(pos));
202 return {.end = end, .error = to_integer_error::none, .value = value};
203}
204
205} // namespace etl::strings
206
207#endif // TETL_STRINGS_TO_INTEGER_HPP
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:19
constexpr auto isdigit(int ch) noexcept -> int
Checks if the given character is one of the 10 decimal digits: 0123456789.
Definition isdigit.hpp:16
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:20
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:27
constexpr auto next(InputIt it, typename iterator_traits< InputIt >::difference_type n=1) -> InputIt
Return the nth successor of iterator it.
Definition next.hpp:15
Definition find.hpp:9
to_integer_error
Definition to_integer.hpp:84
constexpr auto to_integer(string_view str, Int base=Int(10)) noexcept -> to_integer_result< Int >
Definition to_integer.hpp:98
Definition adjacent_find.hpp:9
constexpr auto operator[](size_type pos) const -> const_reference
Returns a const reference to the character at specified location pos.
Definition basic_string_view.hpp:165
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:190
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:196
Definition numeric_limits.hpp:18
Definition to_integer.hpp:79
bool check_overflow
Definition to_integer.hpp:81
bool skip_whitespace
Definition to_integer.hpp:80
Definition to_integer.hpp:91
to_integer_error error
Definition to_integer.hpp:93
Int value
Definition to_integer.hpp:94
char const * end
Definition to_integer.hpp:92