1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef TEST_SUPPORT_CONCAT_MACROS_H
#define TEST_SUPPORT_CONCAT_MACROS_H
#include <cstdio>
#include <string>
#include "assert_macros.h"
#include "test_macros.h"
#ifndef TEST_HAS_NO_LOCALIZATION
# include <concepts>
# include <iterator>
# include <sstream>
#endif
#if TEST_STD_VER > 17
# ifndef TEST_HAS_NO_LOCALIZATION
[[nodiscard]] constexpr bool test_is_high_surrogate(char32_t value) { return value >= 0xd800 && value <= 0xdbff; }
[[nodiscard]] constexpr bool test_is_low_surrogate(char32_t value) { return value >= 0xdc00 && value <= 0xdfff; }
[[nodiscard]] constexpr bool test_is_surrogate(char32_t value) { return value >= 0xd800 && value <= 0xdfff; }
[[nodiscard]] constexpr bool test_is_code_point(char32_t value) { return value <= 0x10ffff; }
[[nodiscard]] constexpr bool test_is_scalar_value(char32_t value) {
return test_is_code_point(value) && !test_is_surrogate(value);
}
inline constexpr char32_t test_replacement_character = U'\ufffd';
template <class InIt, class OutIt>
OutIt test_transcode() = delete;
template <class InIt, class OutIt>
requires(std::output_iterator<OutIt, const char&> && std::same_as<std::iter_value_t<InIt>, char8_t>)
OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
return std::copy(first, last, out_it);
}
template <class OutIt>
requires std::output_iterator<OutIt, const char&>
void test_encode(OutIt& out_it, char16_t value) {
if (value < 0x80)
*out_it++ = static_cast<char>(value);
else if (value < 0x800) {
*out_it++ = static_cast<char>(0b11000000 | (value >> 6));
*out_it++ = static_cast<char>(0b10000000 | (value & 0b00111111));
} else {
*out_it++ = static_cast<char>(0b11100000 | (value >> 12));
*out_it++ = static_cast<char>(0b10000000 | ((value) >> 6 & 0b00111111));
*out_it++ = static_cast<char>(0b10000000 | (value & 0b00111111));
}
}
template <class OutIt>
requires std::output_iterator<OutIt, const char&>
void test_encode(OutIt& out_it, char32_t value) {
if ((value & 0xffff0000) == 0)
test_encode(out_it, static_cast<char16_t>(value));
else {
*out_it++ = static_cast<char>(0b11100000 | (value >> 18));
*out_it++ = static_cast<char>(0b10000000 | ((value) >> 12 & 0b00111111));
*out_it++ = static_cast<char>(0b10000000 | ((value) >> 6 & 0b00111111));
*out_it++ = static_cast<char>(0b10000000 | (value & 0b00111111));
}
}
template <class InIt, class OutIt>
requires(std::output_iterator<OutIt, const char&> &&
(std::same_as<std::iter_value_t<InIt>, char16_t>
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
|| (std::same_as<std::iter_value_t<InIt>, wchar_t> && sizeof(wchar_t) == 2)
# endif
))
OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
while (first != last) {
char32_t value = *first++;
if (test_is_low_surrogate(value)) [[unlikely]] {
test_encode(out_it, static_cast<char16_t>(test_replacement_character));
continue;
}
if (!test_is_high_surrogate(value)) {
test_encode(out_it, static_cast<char16_t>(value));
continue;
}
if (first == last || !test_is_low_surrogate(static_cast<char32_t>(*first))) [[unlikely]] {
test_encode(out_it, static_cast<char16_t>(test_replacement_character));
continue;
}
value -= 0xd800;
value <<= 10;
value += static_cast<char32_t>(*first++) - 0xdc00;
value += 0x10000;
if (test_is_code_point(value)) [[likely]]
test_encode(out_it, value);
else
test_encode(out_it, static_cast<char16_t>(test_replacement_character));
}
return out_it;
}
template <class InIt, class OutIt>
requires(std::output_iterator<OutIt, const char&> &&
(std::same_as<std::iter_value_t<InIt>, char32_t>
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
|| (std::same_as<std::iter_value_t<InIt>, wchar_t> && sizeof(wchar_t) == 4)
# endif
))
OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
while (first != last) {
char32_t value = *first++;
if (test_is_code_point(value)) [[likely]]
test_encode(out_it, value);
else
test_encode(out_it, static_cast<char16_t>(test_replacement_character));
}
return out_it;
}
template <class T>
concept test_streamable = requires(std::stringstream& stream, T&& value) { stream << value; };
template <class R>
concept test_convertable_range = (!test_streamable<R> && requires(R&& value) {
std::basic_string_view{std::begin(value), std::end(value)};
});
template <class T>
concept test_can_concat = test_streamable<T> || test_convertable_range<T>;
template <test_streamable T>
std::ostream& test_concat(std::ostream& stream, T&& value) {
return stream << value;
}
template <test_convertable_range T>
std::ostream& test_concat(std::ostream& stream, T&& value) {
auto b = std::begin(value);
auto e = std::end(value);
if (b != e) {
// When T is an array it's string-literal, remove the NUL terminator.
if constexpr (std::is_array_v<std::remove_cvref_t<T>>) {
--e;
}
test_transcode(b, e, std::ostream_iterator<char>{stream});
}
return stream;
}
# endif // TEST_HAS_NO_LOCALIZATION
// If possible concatenates message for the assertion function, else returns a
// default message. Not being able to stream is not considered an error. For
// example, streaming to std::wcerr doesn't work properly in the CI. Therefore
// the formatting tests should only stream to std::string.
//
// The macro TEST_WRITE_CONCATENATED can be used to evaluate the arguments
// lazily. This useful when using this function in combination with
// assert_macros.h.
template <class... Args>
std::string test_concat_message([[maybe_unused]] Args&&... args) {
# ifndef TEST_HAS_NO_LOCALIZATION
if constexpr ((test_can_concat<Args> && ...)) {
std::stringstream sstr;
((test_concat(sstr, std::forward<Args>(args))), ...);
return sstr.str();
} else
# endif // TEST_HAS_NO_LOCALIZATION
return "Message discarded since it can't be streamed to std::cerr.\n";
}
// Writes its arguments to stderr, using the test_concat_message helper.
# define TEST_WRITE_CONCATENATED(...) [&] { ::test_eprintf("%s", ::test_concat_message(__VA_ARGS__).c_str()); }
#else
// Fallback definition before C++20 that allows using the macro but doesn't provide a very good message.
# define TEST_WRITE_CONCATENATED(...) [&] { ::test_eprintf("%s", TEST_STRINGIZE(__VA_ARGS__)); }
#endif // TEST_STD_VER > 17
#endif // TEST_SUPPORT_CONCAT_MACROS_H
|