1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef SUPPORT_CHARCONV_TEST_HELPERS_H
10 #define SUPPORT_CHARCONV_TEST_HELPERS_H
11 
12 #include <charconv>
13 #include <cassert>
14 #include <limits>
15 #include <numeric>
16 #include <string.h>
17 #include <stdlib.h>
18 
19 #include "test_macros.h"
20 
21 #if TEST_STD_VER < 11
22 #error This file requires C++11
23 #endif
24 
25 using std::false_type;
26 using std::true_type;
27 
28 template <typename To, typename From>
29 constexpr auto
30 is_non_narrowing(From a) -> decltype(To{a}, true_type())
31 {
32     return {};
33 }
34 
35 template <typename To>
36 constexpr auto
37 is_non_narrowing(...) -> false_type
38 {
39     return {};
40 }
41 
42 template <typename X, typename T>
43 constexpr bool
_fits_in(T,true_type,...)44 _fits_in(T, true_type /* non-narrowing*/, ...)
45 {
46     return true;
47 }
48 
49 template <typename X, typename T, typename xl = std::numeric_limits<X>>
50 constexpr bool
_fits_in(T v,false_type,true_type,true_type)51 _fits_in(T v, false_type, true_type /* T signed*/, true_type /* X signed */)
52 {
53     return xl::lowest() <= v && v <= (xl::max)();
54 }
55 
56 template <typename X, typename T, typename xl = std::numeric_limits<X>>
57 constexpr bool
_fits_in(T v,false_type,true_type,false_type)58 _fits_in(T v, false_type, true_type /* T signed */, false_type /* X unsigned*/)
59 {
60     return 0 <= v && typename std::make_unsigned<T>::type(v) <= (xl::max)();
61 }
62 
63 template <typename X, typename T, typename xl = std::numeric_limits<X>>
64 constexpr bool
_fits_in(T v,false_type,false_type,...)65 _fits_in(T v, false_type, false_type /* T unsigned */, ...)
66 {
67     return v <= typename std::make_unsigned<X>::type((xl::max)());
68 }
69 
70 template <typename X, typename T>
71 constexpr bool
fits_in(T v)72 fits_in(T v)
73 {
74   return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), std::is_signed<X>());
75 }
76 
77 template <typename X>
78 struct to_chars_test_base
79 {
80     template <typename T, size_t N, typename... Ts>
testto_chars_test_base81     void test(T v, char const (&expect)[N], Ts... args)
82     {
83         using std::to_chars;
84         std::to_chars_result r;
85 
86         constexpr size_t len = N - 1;
87         static_assert(len > 0, "expected output won't be empty");
88 
89         if (!fits_in<X>(v))
90             return;
91 
92         r = to_chars(buf, buf + len - 1, X(v), args...);
93         assert(r.ptr == buf + len - 1);
94         assert(r.ec == std::errc::value_too_large);
95 
96         r = to_chars(buf, buf + sizeof(buf), X(v), args...);
97         assert(r.ptr == buf + len);
98         assert(r.ec == std::errc{});
99         assert(memcmp(buf, expect, len) == 0);
100     }
101 
102     template <typename... Ts>
test_valueto_chars_test_base103     void test_value(X v, Ts... args)
104     {
105         using std::to_chars;
106         std::to_chars_result r;
107 
108         // Poison the buffer for testing whether a successful std::to_chars
109         // doesn't modify data beyond r.ptr.
110         std::iota(buf, buf + sizeof(buf), char(1));
111         r = to_chars(buf, buf + sizeof(buf), v, args...);
112         assert(r.ec == std::errc{});
113         for (size_t i = r.ptr - buf; i < sizeof(buf); ++i)
114             assert(buf[i] == static_cast<char>(i + 1));
115         *r.ptr = '\0';
116 
117 #ifndef TEST_HAS_NO_INT128
118         if (sizeof(X) == sizeof(__int128_t)) {
119             auto a = fromchars128_impl(buf, r.ptr, args...);
120             assert(v == a);
121         } else
122 #endif
123         {
124             auto a = fromchars_impl(buf, r.ptr, args...);
125             assert(v == a);
126         }
127 
128         auto ep = r.ptr - 1;
129         r = to_chars(buf, ep, v, args...);
130         assert(r.ptr == ep);
131         assert(r.ec == std::errc::value_too_large);
132     }
133 
134 private:
fromchars_implto_chars_test_base135     static long long fromchars_impl(char const* p, char const* ep, int base, true_type)
136     {
137         char* last;
138         auto r = strtoll(p, &last, base);
139         assert(last == ep);
140 
141         return r;
142     }
143 
fromchars_implto_chars_test_base144     static unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type)
145     {
146         char* last;
147         auto r = strtoull(p, &last, base);
148         assert(last == ep);
149 
150         return r;
151     }
152 #ifndef TEST_HAS_NO_INT128
fromchars128_implto_chars_test_base153     static __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type)
154     {
155         char* last;
156         __int128_t r = strtoll(p, &last, base);
157         if(errno != ERANGE) {
158             assert(last == ep);
159             return r;
160         }
161 
162         // When the value doesn't fit in a long long use from_chars. This is
163         // not ideal since it does a round-trip test instead if using an
164         // external source.
165         std::from_chars_result s = std::from_chars(p, ep, r, base);
166         assert(s.ec == std::errc{});
167         assert(s.ptr == ep);
168 
169         return r;
170     }
171 
fromchars128_implto_chars_test_base172     static __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type)
173     {
174         char* last;
175         __uint128_t r = strtoull(p, &last, base);
176         if(errno != ERANGE) {
177             assert(last == ep);
178             return r;
179         }
180 
181         std::from_chars_result s = std::from_chars(p, ep, r, base);
182         assert(s.ec == std::errc{});
183         assert(s.ptr == ep);
184 
185         return r;
186     }
187 
188     static auto fromchars128_impl(char const* p, char const* ep, int base = 10)
189     -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>()))
190     {
191         return fromchars128_impl(p, ep, base, std::is_signed<X>());
192     }
193 
194 #endif
195 
196     static auto fromchars_impl(char const* p, char const* ep, int base = 10)
197     -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>()))
198     {
199         return fromchars_impl(p, ep, base, std::is_signed<X>());
200     }
201 
202     char buf[150];
203 };
204 
205 template <typename X>
206 struct roundtrip_test_base
207 {
208     template <typename T, typename... Ts>
testroundtrip_test_base209     void test(T v, Ts... args)
210     {
211         using std::from_chars;
212         using std::to_chars;
213         std::from_chars_result r2;
214         std::to_chars_result r;
215         X x = 0xc;
216 
217         if (fits_in<X>(v))
218         {
219             r = to_chars(buf, buf + sizeof(buf), v, args...);
220             assert(r.ec == std::errc{});
221 
222             r2 = from_chars(buf, r.ptr, x, args...);
223             assert(r2.ptr == r.ptr);
224             assert(x == X(v));
225         }
226         else
227         {
228             r = to_chars(buf, buf + sizeof(buf), v, args...);
229             assert(r.ec == std::errc{});
230 
231             r2 = from_chars(buf, r.ptr, x, args...);
232 
233             TEST_DIAGNOSTIC_PUSH
234             TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
235 
236             if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value)
237             {
238                 assert(x == 0xc);
239                 assert(r2.ptr == buf);
240                 assert(r2.ec == std::errc::invalid_argument);
241             }
242             else
243             {
244                 assert(x == 0xc);
245                 assert(r2.ptr == r.ptr);
246                 assert(r2.ec == std::errc::result_out_of_range);
247             }
248 
249             TEST_DIAGNOSTIC_POP
250         }
251     }
252 
253 private:
254     char buf[150];
255 };
256 
257 template <typename... T>
258 struct type_list
259 {
260 };
261 
262 template <typename L1, typename L2>
263 struct type_concat;
264 
265 template <typename... Xs, typename... Ys>
266 struct type_concat<type_list<Xs...>, type_list<Ys...>>
267 {
268     using type = type_list<Xs..., Ys...>;
269 };
270 
271 template <typename L1, typename L2>
272 using concat_t = typename type_concat<L1, L2>::type;
273 
274 template <typename L1, typename L2>
275 constexpr auto concat(L1, L2) -> concat_t<L1, L2>
276 {
277     return {};
278 }
279 
280 auto all_signed = type_list<
281     char,
282     signed char,
283     short,
284     int,
285     long,
286     long long
287 #ifndef TEST_HAS_NO_INT128
288     ,
289     __int128_t
290 #endif
291     >();
292 auto all_unsigned = type_list<
293     unsigned char,
294     unsigned short,
295     unsigned int,
296     unsigned long,
297     unsigned long long
298 #ifndef TEST_HAS_NO_INT128
299     ,
300     __uint128_t
301 #endif
302     >();
303 auto integrals = concat(all_signed, all_unsigned);
304 
305 template <template <typename> class Fn, typename... Ts>
306 void
307 run(type_list<Ts...>)
308 {
309     int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};
310     (void)ls;
311 }
312 
313 #endif // SUPPORT_CHARCONV_TEST_HELPERS_H
314