1// Copyright 2022 Peter Dimov
2// Copyright 2023 Matt Borland
3// Distributed under the Boost Software License, Version 1.0.
4// https://www.boost.org/LICENSE_1_0.txt
5
6#include <boost/charconv.hpp>
7#include <boost/core/lightweight_test.hpp>
8#include <system_error>
9#include <type_traits>
10#include <limits>
11#include <cstring>
12#include <cstdint>
13#include <cerrno>
14#include <utility>
15
16#ifdef BOOST_CHARCONV_HAS_INT128
17template <typename T>
18void test_128bit_int()
19{
20 const char* buffer1 = "85070591730234615865843651857942052864"; // 2^126
21 T test_value = 1;
22 test_value = test_value << 126;
23 T v1 = 0;
24 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(s: buffer1), v1);
25 BOOST_TEST(r1.ec == std::errc());
26 BOOST_TEST(v1 == test_value);
27
28 #ifdef __GLIBCXX_TYPE_INT_N_0
29 BOOST_TEST(std::numeric_limits<T>::max() > static_cast<T>(std::numeric_limits<unsigned long long>::max()));
30 #else
31 BOOST_IF_CONSTEXPR (std::is_same<T, boost::int128_type>::value)
32 {
33 BOOST_TEST(BOOST_CHARCONV_INT128_MAX > static_cast<T>(std::numeric_limits<unsigned long long>::max()));
34 }
35 else
36 {
37 BOOST_TEST(BOOST_CHARCONV_UINT128_MAX > static_cast<T>(std::numeric_limits<unsigned long long>::max()));
38 }
39 #endif
40}
41
42template <typename T>
43void test_128bit_overflow();
44
45template <>
46void test_128bit_overflow<boost::int128_type>()
47{
48 const char* buffer1 = "170141183460469231731687303715884105728"; // max + 1
49 boost::int128_type v1 = 1000;
50 auto r1 = boost::charconv::from_chars(first: buffer1, last: buffer1 + std::strlen(s: buffer1), value&: v1);
51 BOOST_TEST(r1.ec == std::errc::result_out_of_range);
52
53 const char* buffer2 = "-170141183460469231731687303715884105729"; // min - 1
54 boost::int128_type v2 = 1000;
55 auto r2 = boost::charconv::from_chars(first: buffer2, last: buffer2 + std::strlen(s: buffer2), value&: v2);
56 BOOST_TEST(r2.ec == std::errc::result_out_of_range);
57}
58
59template <>
60void test_128bit_overflow<boost::uint128_type>()
61{
62 const char* buffer1 = "340282366920938463463374607431768211457"; // max + 1
63 boost::uint128_type v1 = 1000;
64 auto r1 = boost::charconv::from_chars(first: buffer1, last: buffer1 + std::strlen(s: buffer1), value&: v1);
65 BOOST_TEST(r1.ec == std::errc::result_out_of_range);
66
67 const char* buffer2 = "-1"; // min - 1
68 boost::uint128_type v2 = 1000;
69 auto r2 = boost::charconv::from_chars(first: buffer2, last: buffer2 + std::strlen(s: buffer2), value&: v2);
70 BOOST_TEST(r2.ec == std::errc::invalid_argument);
71}
72
73#endif // 128-bit testing
74
75#ifndef BOOST_NO_CXX14_CONSTEXPR
76template <typename T>
77constexpr std::pair<T, boost::charconv::from_chars_result> constexpr_test_helper()
78{
79 const char* buffer1 = "42";
80 T v1 = 0;
81 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + 2, v1);
82
83 return std::make_pair(v1, r1);
84}
85
86template <typename T>
87constexpr void constexpr_test()
88{
89 constexpr auto results = constexpr_test_helper<T>();
90 static_assert(results.second.ec == std::errc(), "No error");
91 static_assert(results.first == 42, "Value is 42");
92}
93
94#endif
95
96template <typename T>
97void base2_test()
98{
99 // Includes leading 0 which should be ignored
100 const char* buffer1 = "0101010";
101 T v1 = 0;
102 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(s: buffer1), v1, 2);
103 BOOST_TEST(r1.ec == std::errc());
104 BOOST_TEST_EQ(v1, static_cast<T>(42));
105}
106
107template <typename T>
108void base16_test()
109{
110 // In base 16 0x and 0X prefixes are not allowed
111 const char* buffer1 = "2a";
112 T v1 = 0;
113 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(s: buffer1), v1, 16);
114 BOOST_TEST(r1.ec == std::errc());
115 BOOST_TEST_EQ(v1, static_cast<T>(42));
116
117 const char* buffer2 = "0";
118 T v2 = 1;
119 auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(s: buffer2), v2, 16);
120 BOOST_TEST(r2.ec == std::errc());
121 BOOST_TEST_EQ(v2, static_cast<T>(0));
122}
123
124template <typename T>
125void overflow_test()
126{
127 const char* buffer1 = "1234";
128 T v1 = 0;
129 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(s: buffer1), v1);
130
131 BOOST_IF_CONSTEXPR((std::numeric_limits<T>::max)() < 1234)
132 {
133 BOOST_TEST(r1.ec == std::errc::result_out_of_range);
134 }
135 else
136 {
137 BOOST_TEST(r1.ec == std::errc()) && BOOST_TEST_EQ(v1, 1234);
138 }
139
140 const char* buffer2 = "123456789123456789123456789";
141 T v2 = 0;
142 auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(s: buffer2), v2);
143 // In the event of overflow v2 is to be returned unmodified
144 BOOST_TEST(r2.ec == std::errc::result_out_of_range) && BOOST_TEST_EQ(v2, static_cast<T>(0));
145}
146
147template <typename T>
148void invalid_argument_test()
149{
150 const char* buffer1 = "";
151 T v1 = 0;
152 auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(s: buffer1), v1);
153 BOOST_TEST(r1.ec == std::errc::invalid_argument);
154
155 const char* buffer2 = "-";
156 T v2 = 0;
157 auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(s: buffer2), v2);
158 BOOST_TEST(r2.ec == std::errc::invalid_argument);
159
160 const char* buffer3 = "+";
161 T v3 = 0;
162 auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(s: buffer3), v3);
163 BOOST_TEST(r3.ec == std::errc::invalid_argument);
164
165 BOOST_IF_CONSTEXPR(std::is_unsigned<T>::value)
166 {
167 const char* buffer4 = "-123";
168 T v4 = 0;
169 auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(s: buffer4), v4);
170 BOOST_TEST(r4.ec == std::errc::invalid_argument);
171 }
172
173 // Bases outside 2-36 inclusive return std::errc::invalid_argument
174 const char* buffer5 = "23";
175 T v5 = 0;
176 auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(s: buffer5), v5, 1);
177 BOOST_TEST(r5.ec == std::errc::invalid_argument);
178 auto r6 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(s: buffer5), v5, 50);
179 BOOST_TEST(r6.ec == std::errc::invalid_argument);
180
181 const char* buffer7 = "+12345";
182 T v7 = 3;
183 auto r7 = boost::charconv::from_chars(buffer7, buffer7 + std::strlen(s: buffer7), v7);
184 BOOST_TEST(r7.ec == std::errc::invalid_argument);
185 BOOST_TEST_EQ(v7, static_cast<T>(3));
186
187 const char* buffer8 = " 12345";
188 T v8 = 3;
189 auto r8 = boost::charconv::from_chars(buffer8, buffer8 + std::strlen(s: buffer8), v8);
190 BOOST_TEST(r8.ec == std::errc::invalid_argument);
191 BOOST_TEST_EQ(v8, static_cast<T>(3));
192
193 const char* buffer9 = "123 45";
194 T v9 = 3;
195 auto r9 = boost::charconv::from_chars(buffer9, buffer9 + std::strlen(s: buffer9), v9);
196 BOOST_TEST(r9);
197 BOOST_TEST_EQ(v9, static_cast<T>(123));
198}
199
200// No overflows, negative numbers, locales, etc.
201template <typename T>
202void simple_test()
203{
204 const char* buffer = "34";
205
206 T v = 0;
207 auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(s: buffer), v);
208
209 BOOST_TEST( r.ec == std::errc() ) && BOOST_TEST_EQ(v, static_cast<T>(34));
210 BOOST_TEST(r == r);
211
212 boost::charconv::from_chars_result r2 {r.ptr, std::errc()};
213 BOOST_TEST(r == r2);
214
215 const char* buffer2 = "12";
216 T v2 = 0;
217 auto r3 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(s: buffer), v2);
218 BOOST_TEST(r != r3);
219 BOOST_TEST(r3.ec == std::errc()) && BOOST_TEST_EQ(v2, static_cast<T>(12));
220}
221
222template <typename T>
223void extended_ascii_codes()
224{
225 const char* buffer = "30±5"; // plus/minus is 177
226 T v = 0;
227 auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(s: buffer), v);
228 BOOST_TEST(r.ec == std::errc()) && BOOST_TEST_EQ(v, static_cast<T>(30));
229
230 const char* buffer2 = "123°"; // Degrees is 186
231 T v2 = 0;
232 auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(s: buffer), v2);
233 BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ(v2, static_cast<T>(123));
234
235 const char* buffer3 = "2¼"; // 1/4 is 188
236 T v3 = 0;
237 auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(s: buffer3), v3);
238 BOOST_TEST(r3.ec == std::errc()) && BOOST_TEST_EQ(v3, static_cast<T>(2));
239
240 const char* buffer4 = "123²"; // squared is 178
241 T v4 = 0;
242 auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(s: buffer4), v4);
243 BOOST_TEST(r4.ec == std::errc()) && BOOST_TEST_EQ(v4, static_cast<T>(123));
244}
245
246int main()
247{
248 simple_test<char>();
249 simple_test<signed char>();
250 simple_test<unsigned char>();
251 simple_test<short>();
252 simple_test<unsigned short>();
253 simple_test<int>();
254 simple_test<unsigned>();
255 simple_test<long>();
256 simple_test<unsigned long>();
257 simple_test<long long>();
258 simple_test<unsigned long long>();
259 simple_test<std::int32_t>();
260 simple_test<std::uint64_t>();
261
262 invalid_argument_test<int>();
263 invalid_argument_test<unsigned>();
264
265 overflow_test<char>();
266 overflow_test<int>();
267
268 base16_test<int>();
269 base16_test<unsigned>();
270
271 base2_test<unsigned char>();
272 base2_test<long>();
273
274 #if !(defined(__GNUC__) && __GNUC__ == 5)
275 # ifndef BOOST_NO_CXX14_CONSTEXPR
276 constexpr_test<int>();
277 # endif
278 #endif
279
280 #ifdef BOOST_CHARCONV_HAS_INT128
281 test_128bit_int<boost::int128_type>();
282 test_128bit_int<boost::uint128_type>();
283
284 test_128bit_overflow<boost::int128_type>();
285 test_128bit_overflow<boost::uint128_type>();
286 #endif
287
288 extended_ascii_codes<int>();
289 extended_ascii_codes<unsigned>();
290 extended_ascii_codes<char>();
291 extended_ascii_codes<unsigned char>();
292
293 return boost::report_errors();
294}
295

source code of boost/libs/charconv/test/from_chars.cpp