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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef TEST_SUPPORT_NASTY_STRING_H
10 #define TEST_SUPPORT_NASTY_STRING_H
11
12 #include <algorithm>
13 #include <cstddef>
14 #include <string>
15 #include <type_traits>
16
17 #include "make_string.h"
18 #include "test_macros.h"
19
20 // This defines a nasty_string similar to nasty_containers. This string's
21 // value_type does operator hijacking, which allows us to ensure that the
22 // library uses the provided `CharTraits` instead of using operations on
23 // the value_type directly.
24
25
26 // When using the code during constant evaluation it relies on
27 // P2647R1 Permitting static constexpr variables in constexpr functions
28 // This is a C++23 feature, which is not supported by all compilers yet.
29 // * GCC >= 13
30 // * Clang >= 16
31 // * MSVC no support yet
32 //
33 // TODO After there is proper compiler support use TEST_STD_VER >= 23 instead
34 // of this macro in the tests.
35 #if TEST_STD_VER < 23 || __cpp_constexpr < 202211L
36 # define TEST_HAS_NO_NASTY_STRING
37 #endif
38
39 #ifndef TEST_HAS_NO_NASTY_STRING
40 // Make sure the char-like operations in strings do not depend on the char-like type.
41 struct nasty_char {
42 template <typename T>
43 friend auto operator<=>(T, T) = delete;
44
45 template <typename T>
46 friend void operator+(T&&) = delete;
47
48 template <typename T>
49 friend void operator-(T&&) = delete;
50
51 template <typename T>
52 friend void operator&(T&&) = delete;
53
54 char c;
55 };
56
57 static_assert(std::is_trivial<nasty_char>::value, "");
58 static_assert(std::is_standard_layout<nasty_char>::value, "");
59
60 // These traits are based on the constexpr_traits test class.
61 struct nasty_char_traits {
62 typedef nasty_char char_type;
63 typedef int int_type;
64 typedef std::streamoff off_type;
65 typedef std::streampos pos_type;
66 typedef std::mbstate_t state_type;
67 // The comparison_category is omitted so the class will have weak_ordering
68 // in C++20. This is intentional.
69
assignnasty_char_traits70 static constexpr void assign(char_type& c1, const char_type& c2) noexcept { c1 = c2; }
71
eqnasty_char_traits72 static constexpr bool eq(char_type c1, char_type c2) noexcept { return c1.c == c2.c; }
73
ltnasty_char_traits74 static constexpr bool lt(char_type c1, char_type c2) noexcept { return c1.c < c2.c; }
75
76 static constexpr int compare(const char_type* s1, const char_type* s2, std::size_t n);
77 static constexpr std::size_t length(const char_type* s);
78 static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& a);
79 static constexpr char_type* move(char_type* s1, const char_type* s2, std::size_t n);
80 static constexpr char_type* copy(char_type* s1, const char_type* s2, std::size_t n);
81 static constexpr char_type* assign(char_type* s, std::size_t n, char_type a);
82
not_eofnasty_char_traits83 static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? ~eof() : c; }
84
to_char_typenasty_char_traits85 static constexpr char_type to_char_type(int_type c) noexcept { return char_type(c); }
86
to_int_typenasty_char_traits87 static constexpr int_type to_int_type(char_type c) noexcept { return int_type(c.c); }
88
eq_int_typenasty_char_traits89 static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept { return c1 == c2; }
90
eofnasty_char_traits91 static constexpr int_type eof() noexcept { return int_type(EOF); }
92 };
93
compare(const nasty_char * s1,const nasty_char * s2,std::size_t n)94 constexpr int nasty_char_traits::compare(const nasty_char* s1, const nasty_char* s2, std::size_t n) {
95 for (; n; --n, ++s1, ++s2) {
96 if (lt(*s1, *s2))
97 return -1;
98 if (lt(*s2, *s1))
99 return 1;
100 }
101 return 0;
102 }
103
length(const nasty_char * s)104 constexpr std::size_t nasty_char_traits::length(const nasty_char* s) {
105 std::size_t len = 0;
106 for (; !eq(*s, nasty_char(0)); ++s)
107 ++len;
108 return len;
109 }
110
find(const nasty_char * s,std::size_t n,const nasty_char & a)111 constexpr const nasty_char* nasty_char_traits::find(const nasty_char* s, std::size_t n, const nasty_char& a) {
112 for (; n; --n) {
113 if (eq(*s, a))
114 return s;
115 ++s;
116 }
117 return 0;
118 }
119
move(nasty_char * s1,const nasty_char * s2,std::size_t n)120 constexpr nasty_char* nasty_char_traits::move(nasty_char* s1, const nasty_char* s2, std::size_t n) {
121 nasty_char* r = s1;
122 if (s1 < s2) {
123 for (; n; --n, ++s1, ++s2)
124 assign(*s1, *s2);
125 } else if (s2 < s1) {
126 s1 += n;
127 s2 += n;
128 for (; n; --n)
129 assign(*--s1, *--s2);
130 }
131 return r;
132 }
133
copy(nasty_char * s1,const nasty_char * s2,std::size_t n)134 constexpr nasty_char* nasty_char_traits::copy(nasty_char* s1, const nasty_char* s2, std::size_t n) {
135 if (!std::is_constant_evaluated()) // fails in constexpr because we might be comparing unrelated pointers
136 assert(s2 < s1 || s2 >= s1 + n);
137 nasty_char* r = s1;
138 for (; n; --n, ++s1, ++s2)
139 assign(*s1, *s2);
140 return r;
141 }
142
assign(nasty_char * s,std::size_t n,nasty_char a)143 constexpr nasty_char* nasty_char_traits::assign(nasty_char* s, std::size_t n, nasty_char a) {
144 nasty_char* r = s;
145 for (; n; --n, ++s)
146 assign(*s, a);
147 return r;
148 }
149
150 using nasty_string = std::basic_string<nasty_char, nasty_char_traits>;
151
152 template <std::size_t N>
153 struct ToNastyChar {
ToNastyCharToNastyChar154 constexpr ToNastyChar(const char (&r)[N]) {
155 std::transform(r, r + N, std::addressof(text[0]), [](char c) { return nasty_char{c}; });
156 }
157 nasty_char text[N];
158 };
159
160 template <std::size_t N>
161 ToNastyChar(const char (&)[N]) -> ToNastyChar<N>;
162
163 template <ToNastyChar t>
to_nasty_char()164 constexpr auto to_nasty_char() {
165 return t;
166 }
167
168 // A macro like MAKE_CSTRING
169 //
170 // The difference is this macro can convert the nasty_char too.
171 //
172 // The lambda is a template, so the 'if constexpr' false branch is not evaluated for the nasty_char.
173 # define CONVERT_TO_CSTRING(CHAR, STR) \
174 []<class CharT> { \
175 if constexpr (std::is_same_v<CharT, nasty_char>) { \
176 static constexpr auto result = to_nasty_char<STR>(); \
177 return result.text; \
178 } else \
179 return MAKE_CSTRING(CharT, STR); \
180 }.template operator()<CHAR>() /* */
181 #else // TEST_HAS_NO_NASTY_STRING
182 # define CONVERT_TO_CSTRING(CharT, STR) MAKE_CSTRING(CharT, STR)
183 #endif // TEST_HAS_NO_NASTY_STRING
184
185 #endif // TEST_SUPPORT_NASTY_STRING_H
186