331 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef SUPPORT_CHARCONV_TEST_HELPERS_H10#define SUPPORT_CHARCONV_TEST_HELPERS_H11 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 < 1126#error This file requires C++1127#endif28 29using std::false_type;30using std::true_type;31 32template <typename To, typename From>33constexpr auto34is_non_narrowing(From a) -> decltype(To{a}, true_type())35{36 return {};37}38 39template <typename To>40constexpr auto41is_non_narrowing(...) -> false_type42{43 return {};44}45 46template <typename X, typename T>47constexpr bool48_fits_in(T, true_type /* non-narrowing*/, ...)49{50 return true;51}52 53template <typename X, typename T, typename xl = std::numeric_limits<X>>54constexpr bool55_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 60template <typename X, typename T, typename xl = std::numeric_limits<X>>61constexpr bool62_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 67template <typename X, typename T, typename xl = std::numeric_limits<X>>68constexpr bool69_fits_in(T v, false_type, false_type /* T unsigned */, ...)70{71 return v <= typename std::make_unsigned<X>::type((xl::max)());72}73 74template <typename X, typename T>75constexpr bool76fits_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 81template <typename X>82struct to_chars_test_base83{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_chars111 // doesn't modify data beyond r.ptr. Use unsigned values to avoid112 // 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_INT128121 if (sizeof(X) == sizeof(__int128_t)) {122 auto a = fromchars128_impl(buf, r.ptr, args...);123 assert(v == a);124 } else125#endif126 {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 137private: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 else145 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 else158 r = strtoull(p, &last, base);159 assert(last == ep);160 161 return r;162 }163#ifndef TEST_HAS_NO_INT128164 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 is176 // not ideal since it does a round-trip test instead if using an177 // 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#endif212 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 222template <typename X>223struct roundtrip_test_base224{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 else242 {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_PUSH249 TEST_MSVC_DIAGNOSTIC_IGNORED(4127) // conditional expression is constant250 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 else258 {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_POP265 }266 }267 268private:269 char buf[150];270};271 272template <typename... T>273struct type_list274{275};276 277template <typename L1, typename L2>278struct type_concat;279 280template <typename... Xs, typename... Ys>281struct type_concat<type_list<Xs...>, type_list<Ys...>>282{283 using type = type_list<Xs..., Ys...>;284};285 286template <typename L1, typename L2>287using concat_t = typename type_concat<L1, L2>::type;288 289template <typename L1, typename L2>290constexpr auto concat(L1, L2) -> concat_t<L1, L2>291{292 return {};293}294 295auto all_signed = type_list<296 char,297 signed char,298 short,299 int,300 long,301 long long302#ifndef TEST_HAS_NO_INT128303 ,304 __int128_t305#endif306 >();307auto all_unsigned = type_list<308 unsigned char,309 unsigned short,310 unsigned int,311 unsigned long,312 unsigned long long313#ifndef TEST_HAS_NO_INT128314 ,315 __uint128_t316#endif317 >();318auto integrals = concat(all_signed, all_unsigned);319 320auto all_floats = type_list< float, double >(); //TODO: Add long double321 322template <template <typename> class Fn, typename... Ts>323TEST_CONSTEXPR_CXX23 void324run(type_list<Ts...>)325{326 int ls[sizeof...(Ts)] = {(Fn<Ts>{}(), 0)...};327 (void)ls;328}329 330#endif // SUPPORT_CHARCONV_TEST_HELPERS_H331