File: concat_macros.h

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 1,998,492 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (198 lines) | stat: -rw-r--r-- 6,844 bytes parent folder | download | duplicates (5)
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