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