xref: /llvm-project/libcxx/test/support/charconv_test_helpers.h (revision 14324fa4285f5cd1e421a6cebdceb05d6c49a8dc)
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
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
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
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
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
72 fits_in(T v)
73 {
74     return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(),
75                        std::is_signed<X>());
76 }
77 
78 template <typename X>
79 struct to_chars_test_base
80 {
81     template <typename T, size_t N, typename... Ts>
82     void test(T v, char const (&expect)[N], Ts... args)
83     {
84         using std::to_chars;
85         std::to_chars_result r;
86 
87         constexpr size_t len = N - 1;
88         static_assert(len > 0, "expected output won't be empty");
89 
90         if (!fits_in<X>(v))
91             return;
92 
93         r = to_chars(buf, buf + len - 1, X(v), args...);
94         assert(r.ptr == buf + len - 1);
95         assert(r.ec == std::errc::value_too_large);
96 
97         r = to_chars(buf, buf + sizeof(buf), X(v), args...);
98         assert(r.ptr == buf + len);
99         assert(r.ec == std::errc{});
100         assert(memcmp(buf, expect, len) == 0);
101     }
102 
103     template <typename... Ts>
104     void test_value(X v, Ts... args)
105     {
106         using std::to_chars;
107         std::to_chars_result r;
108 
109         // Poison the buffer for testing whether a successful std::to_chars
110         // doesn't modify data beyond r.ptr.
111         std::iota(buf, buf + sizeof(buf), char(1));
112         r = to_chars(buf, buf + sizeof(buf), v, args...);
113         assert(r.ec == std::errc{});
114         for (size_t i = r.ptr - buf; i < sizeof(buf); ++i)
115             assert(buf[i] == static_cast<char>(i + 1));
116         *r.ptr = '\0';
117 
118         auto a = fromchars(buf, r.ptr, args...);
119         assert(v == a);
120 
121         auto ep = r.ptr - 1;
122         r = to_chars(buf, ep, v, args...);
123         assert(r.ptr == ep);
124         assert(r.ec == std::errc::value_too_large);
125     }
126 
127 private:
128     static long long fromchars(char const* p, char const* ep, int base, true_type)
129     {
130         char* last;
131         auto r = strtoll(p, &last, base);
132         assert(last == ep);
133 
134         return r;
135     }
136 
137     static unsigned long long fromchars(char const* p, char const* ep, int base, false_type)
138     {
139         char* last;
140         auto r = strtoull(p, &last, base);
141         assert(last == ep);
142 
143         return r;
144     }
145 
146     static auto fromchars(char const* p, char const* ep, int base = 10)
147     -> decltype(fromchars(p, ep, base, std::is_signed<X>()))
148     {
149         return fromchars(p, ep, base, std::is_signed<X>());
150     }
151 
152     char buf[100];
153 };
154 
155 template <typename X>
156 struct roundtrip_test_base
157 {
158     template <typename T, typename... Ts>
159     void test(T v, Ts... args)
160     {
161         using std::from_chars;
162         using std::to_chars;
163         std::from_chars_result r2;
164         std::to_chars_result r;
165         X x = 0xc;
166 
167         if (fits_in<X>(v))
168         {
169             r = to_chars(buf, buf + sizeof(buf), v, args...);
170             assert(r.ec == std::errc{});
171 
172             r2 = from_chars(buf, r.ptr, x, args...);
173             assert(r2.ptr == r.ptr);
174             assert(x == X(v));
175         }
176         else
177         {
178             r = to_chars(buf, buf + sizeof(buf), v, args...);
179             assert(r.ec == std::errc{});
180 
181             r2 = from_chars(buf, r.ptr, x, args...);
182 
183             TEST_DIAGNOSTIC_PUSH
184             TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant
185 
186             if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value)
187             {
188                 assert(x == 0xc);
189                 assert(r2.ptr == buf);
190                 assert(r2.ec == std::errc::invalid_argument);
191             }
192             else
193             {
194                 assert(x == 0xc);
195                 assert(r2.ptr == r.ptr);
196                 assert(r2.ec == std::errc::result_out_of_range);
197             }
198 
199             TEST_DIAGNOSTIC_POP
200         }
201     }
202 
203 private:
204     char buf[100];
205 };
206 
207 template <typename... T>
208 struct type_list
209 {
210 };
211 
212 template <typename L1, typename L2>
213 struct type_concat;
214 
215 template <typename... Xs, typename... Ys>
216 struct type_concat<type_list<Xs...>, type_list<Ys...>>
217 {
218     using type = type_list<Xs..., Ys...>;
219 };
220 
221 template <typename L1, typename L2>
222 using concat_t = typename type_concat<L1, L2>::type;
223 
224 template <typename L1, typename L2>
225 constexpr auto concat(L1, L2) -> concat_t<L1, L2>
226 {
227     return {};
228 }
229 
230 auto all_signed = type_list<char, signed char, short, int, long, long long>();
231 auto all_unsigned = type_list<unsigned char, unsigned short, unsigned int,
232                               unsigned long, unsigned long long>();
233 auto integrals = concat(all_signed, all_unsigned);
234 
235 template <template <typename> class Fn, typename... Ts>
236 void
237 run(type_list<Ts...>)
238 {
239     int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};
240     (void)ls;
241 }
242 
243 #endif // SUPPORT_CHARCONV_TEST_HELPERS_H
244