1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef TEST_SUPPORT_CONCAT_MACROS_H
10 #define TEST_SUPPORT_CONCAT_MACROS_H
15 #include "assert_macros.h"
16 #include "test_macros.h"
18 #ifndef TEST_HAS_NO_LOCALIZATION
26 # ifndef TEST_HAS_NO_LOCALIZATION
28 [[nodiscard
]] constexpr bool test_is_high_surrogate(char32_t value
) { return value
>= 0xd800 && value
<= 0xdbff; }
30 [[nodiscard
]] constexpr bool test_is_low_surrogate(char32_t value
) { return value
>= 0xdc00 && value
<= 0xdfff; }
32 [[nodiscard
]] constexpr bool test_is_surrogate(char32_t value
) { return value
>= 0xd800 && value
<= 0xdfff; }
34 [[nodiscard
]] constexpr bool test_is_code_point(char32_t value
) { return value
<= 0x10ffff; }
36 [[nodiscard
]] constexpr bool test_is_scalar_value(char32_t value
) {
37 return test_is_code_point(value
) && !test_is_surrogate(value
);
40 inline constexpr char32_t test_replacement_character
= U
'\ufffd';
42 template <class InIt
, class OutIt
>
43 OutIt
test_transcode() = delete;
45 template <class InIt
, class OutIt
>
46 requires(std::output_iterator
<OutIt
, const char&> && std::same_as
<std::iter_value_t
<InIt
>, char8_t
>)
47 OutIt
test_transcode(InIt first
, InIt last
, OutIt out_it
) {
48 return std::copy(first
, last
, out_it
);
51 template <class OutIt
>
52 requires
std::output_iterator
<OutIt
, const char&>
53 void test_encode(OutIt
& out_it
, char16_t value
) {
55 *out_it
++ = static_cast<char>(value
);
56 else if (value
< 0x800) {
57 *out_it
++ = static_cast<char>(0b11000000 | (value
>> 6));
58 *out_it
++ = static_cast<char>(0b10000000 | (value
& 0b00111111));
60 *out_it
++ = static_cast<char>(0b11100000 | (value
>> 12));
61 *out_it
++ = static_cast<char>(0b10000000 | ((value
) >> 6 & 0b00111111));
62 *out_it
++ = static_cast<char>(0b10000000 | (value
& 0b00111111));
66 template <class OutIt
>
67 requires
std::output_iterator
<OutIt
, const char&>
68 void test_encode(OutIt
& out_it
, char32_t value
) {
69 if ((value
& 0xffff0000) == 0)
70 test_encode(out_it
, static_cast<char16_t
>(value
));
72 *out_it
++ = static_cast<char>(0b11100000 | (value
>> 18));
73 *out_it
++ = static_cast<char>(0b10000000 | ((value
) >> 12 & 0b00111111));
74 *out_it
++ = static_cast<char>(0b10000000 | ((value
) >> 6 & 0b00111111));
75 *out_it
++ = static_cast<char>(0b10000000 | (value
& 0b00111111));
79 template <class InIt
, class OutIt
>
80 requires(std::output_iterator
<OutIt
, const char&> &&
81 (std::same_as
<std::iter_value_t
<InIt
>, char16_t
>
82 # ifndef TEST_HAS_NO_WIDE_CHARACTERS
83 || (std::same_as
<std::iter_value_t
<InIt
>, wchar_t> && sizeof(wchar_t) == 2)
86 OutIt
test_transcode(InIt first
, InIt last
, OutIt out_it
) {
87 while (first
!= last
) {
88 char32_t value
= *first
++;
90 if (test_is_low_surrogate(value
)) [[unlikely
]] {
91 test_encode(out_it
, static_cast<char16_t
>(test_replacement_character
));
95 if (!test_is_high_surrogate(value
)) {
96 test_encode(out_it
, static_cast<char16_t
>(value
));
100 if (first
== last
|| !test_is_low_surrogate(static_cast<char32_t
>(*first
))) [[unlikely
]] {
101 test_encode(out_it
, static_cast<char16_t
>(test_replacement_character
));
107 value
+= static_cast<char32_t
>(*first
++) - 0xdc00;
110 if (test_is_code_point(value
)) [[likely
]]
111 test_encode(out_it
, value
);
113 test_encode(out_it
, static_cast<char16_t
>(test_replacement_character
));
119 template <class InIt
, class OutIt
>
120 requires(std::output_iterator
<OutIt
, const char&> &&
121 (std::same_as
<std::iter_value_t
<InIt
>, char32_t
>
122 # ifndef TEST_HAS_NO_WIDE_CHARACTERS
123 || (std::same_as
<std::iter_value_t
<InIt
>, wchar_t> && sizeof(wchar_t) == 4)
126 OutIt
test_transcode(InIt first
, InIt last
, OutIt out_it
) {
127 while (first
!= last
) {
128 char32_t value
= *first
++;
129 if (test_is_code_point(value
)) [[likely
]]
130 test_encode(out_it
, value
);
132 test_encode(out_it
, static_cast<char16_t
>(test_replacement_character
));
138 concept test_streamable
= requires(std::stringstream
& stream
, T
&& value
) { stream
<< value
; };
141 concept test_convertable_range
= (!test_streamable
<R
> && requires(R
&& value
) {
142 std::basic_string_view
{std::begin(value
), std::end(value
)};
146 concept test_can_concat
= test_streamable
<T
> || test_convertable_range
<T
>;
148 template <test_streamable T
>
149 std::ostream
& test_concat(std::ostream
& stream
, T
&& value
) {
150 return stream
<< value
;
153 template <test_convertable_range T
>
154 std::ostream
& test_concat(std::ostream
& stream
, T
&& value
) {
155 auto b
= std::begin(value
);
156 auto e
= std::end(value
);
158 // When T is an array it's string-literal, remove the NUL terminator.
159 if constexpr (std::is_array_v
<std::remove_cvref_t
<T
>>) {
162 test_transcode(b
, e
, std::ostream_iterator
<char>{stream
});
166 # endif // TEST_HAS_NO_LOCALIZATION
168 // If possible concatenates message for the assertion function, else returns a
169 // default message. Not being able to stream is not considered an error. For
170 // example, streaming to std::wcerr doesn't work properly in the CI. Therefore
171 // the formatting tests should only stream to std::string.
173 // The macro TEST_WRITE_CONCATENATED can be used to evaluate the arguments
174 // lazily. This useful when using this function in combination with
176 template <class... Args
>
177 std::string
test_concat_message([[maybe_unused
]] Args
&&... args
) {
178 # ifndef TEST_HAS_NO_LOCALIZATION
179 if constexpr ((test_can_concat
<Args
> && ...)) {
180 std::stringstream sstr
;
181 ((test_concat(sstr
, std::forward
<Args
>(args
))), ...);
184 # endif // TEST_HAS_NO_LOCALIZATION
185 return "Message discarded since it can't be streamed to std::cerr.\n";
188 // Writes its arguments to stderr, using the test_concat_message helper.
189 # define TEST_WRITE_CONCATENATED(...) [&] { ::test_eprintf("%s", ::test_concat_message(__VA_ARGS__).c_str()); }
193 // Fallback definition before C++20 that allows using the macro but doesn't provide a very good message.
194 # define TEST_WRITE_CONCATENATED(...) [&] { ::test_eprintf("%s", TEST_STRINGIZE(__VA_ARGS__)); }
196 #endif // TEST_STD_VER > 17
198 #endif // TEST_SUPPORT_CONCAT_MACROS_H