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 <algorithm> 13 #include <charconv> 14 #include <cassert> 15 #include <limits> 16 #include <numeric> 17 #include <string.h> 18 #include <stdlib.h> 19 20 #include "test_macros.h" 21 22 #if TEST_STD_VER < 11 23 #error This file requires C++11 24 #endif 25 26 using std::false_type; 27 using std::true_type; 28 29 template <typename To, typename From> 30 constexpr auto 31 is_non_narrowing(From a) -> decltype(To{a}, true_type()) 32 { 33 return {}; 34 } 35 36 template <typename To> 37 constexpr auto 38 is_non_narrowing(...) -> false_type 39 { 40 return {}; 41 } 42 43 template <typename X, typename T> 44 constexpr bool 45 _fits_in(T, true_type /* non-narrowing*/, ...) 46 { 47 return true; 48 } 49 50 template <typename X, typename T, typename xl = std::numeric_limits<X>> 51 constexpr bool 52 _fits_in(T v, false_type, true_type /* T signed*/, true_type /* X signed */) 53 { 54 return xl::lowest() <= v && v <= (xl::max)(); 55 } 56 57 template <typename X, typename T, typename xl = std::numeric_limits<X>> 58 constexpr bool 59 _fits_in(T v, false_type, true_type /* T signed */, false_type /* X unsigned*/) 60 { 61 return 0 <= v && typename std::make_unsigned<T>::type(v) <= (xl::max)(); 62 } 63 64 template <typename X, typename T, typename xl = std::numeric_limits<X>> 65 constexpr bool 66 _fits_in(T v, false_type, false_type /* T unsigned */, ...) 67 { 68 return v <= typename std::make_unsigned<X>::type((xl::max)()); 69 } 70 71 template <typename X, typename T> 72 constexpr bool 73 fits_in(T v) 74 { 75 return _fits_in<X>(v, is_non_narrowing<X>(v), std::is_signed<T>(), 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 TEST_CONSTEXPR_CXX23 void test(T v, char const (&expect)[N], Ts... args) 83 { 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 = std::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 = std::to_chars(buf, buf + sizeof(buf), X(v), args...); 97 assert(r.ptr == buf + len); 98 assert(r.ec == std::errc{}); 99 assert(std::equal(buf, buf + len, expect)); 100 } 101 102 template <typename... Ts> 103 TEST_CONSTEXPR_CXX23 void test_value(X v, Ts... args) 104 { 105 std::to_chars_result r; 106 107 // Poison the buffer for testing whether a successful std::to_chars 108 // doesn't modify data beyond r.ptr. Use unsigned values to avoid 109 // overflowing char when it's signed. 110 std::iota(buf, buf + sizeof(buf), static_cast<unsigned char>(1)); 111 r = std::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(static_cast<unsigned char>(buf[i]) == 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 = std::to_chars(buf, ep, v, args...); 130 assert(r.ptr == ep); 131 assert(r.ec == std::errc::value_too_large); 132 } 133 134 private: 135 static TEST_CONSTEXPR_CXX23 long long fromchars_impl(char const* p, char const* ep, int base, true_type) 136 { 137 char* last; 138 long long r; 139 if (TEST_IS_CONSTANT_EVALUATED) 140 last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr); 141 else 142 r = strtoll(p, &last, base); 143 assert(last == ep); 144 145 return r; 146 } 147 148 static TEST_CONSTEXPR_CXX23 unsigned long long fromchars_impl(char const* p, char const* ep, int base, false_type) 149 { 150 char* last; 151 unsigned long long r; 152 if (TEST_IS_CONSTANT_EVALUATED) 153 last = const_cast<char*>(std::from_chars(p, ep, r, base).ptr); 154 else 155 r = strtoull(p, &last, base); 156 assert(last == ep); 157 158 return r; 159 } 160 #ifndef TEST_HAS_NO_INT128 161 static TEST_CONSTEXPR_CXX23 __int128_t fromchars128_impl(char const* p, char const* ep, int base, true_type) 162 { 163 if (!TEST_IS_CONSTANT_EVALUATED) { 164 char* last; 165 __int128_t r = strtoll(p, &last, base); 166 if(errno != ERANGE) { 167 assert(last == ep); 168 return r; 169 } 170 } 171 172 // When the value doesn't fit in a long long use from_chars. This is 173 // not ideal since it does a round-trip test instead if using an 174 // external source. 175 __int128_t r; 176 std::from_chars_result s = std::from_chars(p, ep, r, base); 177 assert(s.ec == std::errc{}); 178 assert(s.ptr == ep); 179 180 return r; 181 } 182 183 static TEST_CONSTEXPR_CXX23 __uint128_t fromchars128_impl(char const* p, char const* ep, int base, false_type) 184 { 185 if (!TEST_IS_CONSTANT_EVALUATED) { 186 char* last; 187 __uint128_t r = strtoull(p, &last, base); 188 if(errno != ERANGE) { 189 assert(last == ep); 190 return r; 191 } 192 } 193 194 __uint128_t r; 195 std::from_chars_result s = std::from_chars(p, ep, r, base); 196 assert(s.ec == std::errc{}); 197 assert(s.ptr == ep); 198 199 return r; 200 } 201 202 static TEST_CONSTEXPR_CXX23 auto fromchars128_impl(char const* p, char const* ep, int base = 10) 203 -> decltype(fromchars128_impl(p, ep, base, std::is_signed<X>())) 204 { 205 return fromchars128_impl(p, ep, base, std::is_signed<X>()); 206 } 207 208 #endif 209 210 static TEST_CONSTEXPR_CXX23 auto fromchars_impl(char const* p, char const* ep, int base = 10) 211 -> decltype(fromchars_impl(p, ep, base, std::is_signed<X>())) 212 { 213 return fromchars_impl(p, ep, base, std::is_signed<X>()); 214 } 215 216 char buf[150]; 217 }; 218 219 template <typename X> 220 struct roundtrip_test_base 221 { 222 template <typename T, typename... Ts> 223 TEST_CONSTEXPR_CXX23 void test(T v, Ts... args) 224 { 225 std::from_chars_result r2; 226 std::to_chars_result r; 227 X x = 0xc; 228 229 if (fits_in<X>(v)) 230 { 231 r = std::to_chars(buf, buf + sizeof(buf), v, args...); 232 assert(r.ec == std::errc{}); 233 234 r2 = std::from_chars(buf, r.ptr, x, args...); 235 assert(r2.ptr == r.ptr); 236 assert(x == X(v)); 237 } 238 else 239 { 240 r = std::to_chars(buf, buf + sizeof(buf), v, args...); 241 assert(r.ec == std::errc{}); 242 243 r2 = std::from_chars(buf, r.ptr, x, args...); 244 245 TEST_DIAGNOSTIC_PUSH 246 TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant 247 248 if (std::is_signed<T>::value && v < 0 && std::is_unsigned<X>::value) 249 { 250 assert(x == 0xc); 251 assert(r2.ptr == buf); 252 assert(r2.ec == std::errc::invalid_argument); 253 } 254 else 255 { 256 assert(x == 0xc); 257 assert(r2.ptr == r.ptr); 258 assert(r2.ec == std::errc::result_out_of_range); 259 } 260 261 TEST_DIAGNOSTIC_POP 262 } 263 } 264 265 private: 266 char buf[150]; 267 }; 268 269 template <typename... T> 270 struct type_list 271 { 272 }; 273 274 template <typename L1, typename L2> 275 struct type_concat; 276 277 template <typename... Xs, typename... Ys> 278 struct type_concat<type_list<Xs...>, type_list<Ys...>> 279 { 280 using type = type_list<Xs..., Ys...>; 281 }; 282 283 template <typename L1, typename L2> 284 using concat_t = typename type_concat<L1, L2>::type; 285 286 template <typename L1, typename L2> 287 constexpr auto concat(L1, L2) -> concat_t<L1, L2> 288 { 289 return {}; 290 } 291 292 auto all_signed = type_list< 293 char, 294 signed char, 295 short, 296 int, 297 long, 298 long long 299 #ifndef TEST_HAS_NO_INT128 300 , 301 __int128_t 302 #endif 303 >(); 304 auto all_unsigned = type_list< 305 unsigned char, 306 unsigned short, 307 unsigned int, 308 unsigned long, 309 unsigned long long 310 #ifndef TEST_HAS_NO_INT128 311 , 312 __uint128_t 313 #endif 314 >(); 315 auto integrals = concat(all_signed, all_unsigned); 316 317 template <template <typename> class Fn, typename... Ts> 318 TEST_CONSTEXPR_CXX23 void 319 run(type_list<Ts...>) 320 { 321 int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...}; 322 (void)ls; 323 } 324 325 #endif // SUPPORT_CHARCONV_TEST_HELPERS_H 326