16
16
#include " test_macros.h"
17
17
18
18
#ifndef TEST_HAS_NO_LOCALIZATION
19
+ # include < concepts>
20
+ # include < iterator>
19
21
# include < sstream>
20
22
#endif
21
23
22
24
#if TEST_STD_VER > 17
23
25
24
26
# ifndef TEST_HAS_NO_LOCALIZATION
27
+
28
+ [[nodiscard]] constexpr bool test_is_high_surrogate (char32_t value) { return value >= 0xd800 && value <= 0xdbff ; }
29
+
30
+ [[nodiscard]] constexpr bool test_is_low_surrogate (char32_t value) { return value >= 0xdc00 && value <= 0xdfff ; }
31
+
32
+ [[nodiscard]] constexpr bool test_is_surrogate (char32_t value) { return value >= 0xd800 && value <= 0xdfff ; }
33
+
34
+ [[nodiscard]] constexpr bool test_is_code_point (char32_t value) { return value <= 0x10ffff ; }
35
+
36
+ [[nodiscard]] constexpr bool test_is_scalar_value (char32_t value) {
37
+ return test_is_code_point (value) && !test_is_surrogate (value);
38
+ }
39
+
40
+ inline constexpr char32_t test_replacement_character = U' \ufffd ' ;
41
+
42
+ template <class InIt , class OutIt >
43
+ OutIt test_transcode () = delete;
44
+
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);
49
+ }
50
+
51
+ template <class OutIt >
52
+ requires std::output_iterator<OutIt, const char &>
53
+ void test_encode (OutIt& out_it, char16_t value) {
54
+ if (value < 0x80 )
55
+ *out_it++ = value;
56
+ else if (value < 0x800 ) {
57
+ *out_it++ = 0b11000000 | (value >> 6 );
58
+ *out_it++ = 0b10000000 | (value & 0b00111111 );
59
+ } else {
60
+ *out_it++ = 0b11100000 | (value >> 12 );
61
+ *out_it++ = 0b10000000 | ((value) >> 6 & 0b00111111 );
62
+ *out_it++ = 0b10000000 | (value & 0b00111111 );
63
+ }
64
+ }
65
+
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));
71
+ else {
72
+ *out_it++ = 0b11100000 | (value >> 18 );
73
+ *out_it++ = 0b10000000 | ((value) >> 12 & 0b00111111 );
74
+ *out_it++ = 0b10000000 | ((value) >> 6 & 0b00111111 );
75
+ *out_it++ = 0b10000000 | (value & 0b00111111 );
76
+ }
77
+ }
78
+
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 ))
84
+ # endif
85
+ )
86
+ OutIt test_transcode(InIt first, InIt last, OutIt out_it) {
87
+ while (first != last) {
88
+ char32_t value = *first++;
89
+
90
+ if (test_is_low_surrogate (value)) [[unlikely]] {
91
+ test_encode (out_it, static_cast <char16_t >(test_replacement_character));
92
+ continue ;
93
+ }
94
+
95
+ if (!test_is_high_surrogate (value)) {
96
+ test_encode (out_it, static_cast <char16_t >(value));
97
+ continue ;
98
+ }
99
+
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));
102
+ continue ;
103
+ }
104
+
105
+ value -= 0xd800 ;
106
+ value <<= 10 ;
107
+ value += static_cast <char32_t >(*first++) - 0xdc00 ;
108
+ value += 0x10000 ;
109
+
110
+ if (test_is_code_point (value)) [[likely]]
111
+ test_encode (out_it, value);
112
+ else
113
+ test_encode (out_it, static_cast <char16_t >(test_replacement_character));
114
+ }
115
+
116
+ return out_it;
117
+ }
118
+
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 ))
124
+ # endif
125
+ )
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);
131
+ else
132
+ test_encode (out_it, static_cast <char16_t >(test_replacement_character));
133
+ }
134
+ return out_it;
135
+ }
136
+
137
+ template <class T >
138
+ concept test_streamable = requires (std::stringstream& stream, T&& value) { stream << value; };
139
+
140
+ template <class R >
141
+ concept test_convertable_range = (!test_streamable<R> && requires (R&& value) {
142
+ std::basic_string_view{std::begin (value), std::end (value)};
143
+ });
144
+
25
145
template <class T >
26
- concept test_char_streamable = requires (T&& value) { std::stringstream{} << std::forward<T>(value); };
27
- # endif
146
+ concept test_can_concat = test_streamable<T> || test_convertable_range<T>;
147
+
148
+ template <test_streamable T>
149
+ std::ostream& test_concat (std::ostream& stream, T&& value) {
150
+ return stream << value;
151
+ }
152
+
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);
157
+ if (b != e) {
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>>) {
160
+ --e;
161
+ }
162
+ test_transcode (b, e, std::ostream_iterator<char >{stream});
163
+ }
164
+ return stream;
165
+ }
166
+ # endif // TEST_HAS_NO_LOCALIZATION
28
167
29
168
// If possible concatenates message for the assertion function, else returns a
30
169
// default message. Not being able to stream is not considered an error. For
@@ -37,12 +176,12 @@ concept test_char_streamable = requires(T&& value) { std::stringstream{} << std:
37
176
template <class ... Args>
38
177
std::string test_concat_message ([[maybe_unused]] Args&&... args) {
39
178
# ifndef TEST_HAS_NO_LOCALIZATION
40
- if constexpr ((test_char_streamable <Args> && ...)) {
179
+ if constexpr ((test_can_concat <Args> && ...)) {
41
180
std::stringstream sstr;
42
- ((sstr << std::forward<Args>(args)), ...);
181
+ ((test_concat ( sstr, std::forward<Args>(args) )), ...);
43
182
return sstr.str ();
44
183
} else
45
- # endif
184
+ # endif // TEST_HAS_NO_LOCALIZATION
46
185
return " Message discarded since it can't be streamed to std::cerr.\n " ;
47
186
}
48
187
0 commit comments