GCC Code Coverage Report


Directory: include/kumi/
File: include/kumi/utils/str.hpp
Date: 2026-10-14 16:33:56
Exec Total Coverage
Lines: 84 86 97.7%
Functions: 14 14 100.0%
Branches: 168 228 73.7%

Line Branch Exec Source
1 //======================================================================================================================
2 /*
3 KUMI - Compact Tuple Tools
4 Copyright : KUMI Project Contributors
5 SPDX-License-Identifier: BSL-1.0
6 */
7 //======================================================================================================================
8 #pragma once
9
10 namespace kumi
11 {
12 /// @brief This is a special value equal to the largest value representable by the type std::size_t.
13 inline constexpr auto npos = static_cast<std::size_t>(-1);
14
15 //====================================================================================================================
16 /**
17 @ingroup kumi_types
18 @class str
19 @brief Static string used to create named fields.
20
21 @tparam N Number of characters in the string (the storage holds N + 1 chars, for the null terminator).
22
23 @note the `.` character is reserved for internal manipulation.
24
25 kumi::str provides a way to define compile time names. A literal "abc" is deduced as kumi::str<3>.
26 Member functions taking another string accept a character array (`char const (&)[M]`, M including the
27 terminator), so they work with literals as well as with `other.data_`.
28 **/
29 //====================================================================================================================
30 template<std::size_t N> struct str
31 {
32 static constexpr char separator = '.';
33
34 char data_[N + 1] = {0};
35
36 1 constexpr str() = default;
37
38 KUMI_ABI constexpr str(char const (&s)[N + 1])
39 {
40 for (std::size_t i = 0; i < N + 1; ++i) data_[i] = s[i];
41 }
42
43 13 KUMI_ABI constexpr std::size_t size() const noexcept { return N; }
44
45 1 KUMI_ABI constexpr auto data() const noexcept { return data_; }
46
47 template<typename T>
48 requires requires { T{data_, std::size_t{N}}; }
49 KUMI_ABI constexpr auto as() const
50 {
51 return T{data_, std::size_t{N}};
52 }
53
54 template<typename CharT, typename Traits>
55 773 friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, str const& s) noexcept
56 {
57 773 os << '\'';
58
2/2
✓ Branch 1 taken 934 times.
✓ Branch 2 taken 594 times.
2166 for (std::size_t i = 0; i < N; ++i) os << s.data_[i];
59 773 return os << '\'';
60 }
61
62 // The size of the result depends on the arguments, so they are passed as std::integral_constant.
63 template<kumi::concepts::index Pos, kumi::concepts::index Count>
64 KUMI_ABI constexpr auto substr(Pos p, Count c) const
65 {
66 constexpr std::size_t pos = static_cast<std::size_t>(p);
67 constexpr std::size_t count = static_cast<std::size_t>(c);
68 static_assert(pos <= N, "Out of range");
69 constexpr std::size_t len = (count == kumi::npos || pos + count > N) ? (N - pos) : count;
70 str<len> res{};
71 for (std::size_t i = 0; i < len; ++i) res.data_[i] = data_[pos + i];
72 return res;
73 }
74
75 template<kumi::concepts::index Pos> KUMI_ABI constexpr auto substr(Pos p) const
76 {
77 return substr(p, std::integral_constant<std::size_t, kumi::npos>{});
78 }
79
80 KUMI_ABI constexpr auto substr() const { return substr(std::integral_constant<std::size_t, 0>{}); }
81
82 template<kumi::concepts::index Size> KUMI_ABI constexpr auto remove_prefix(Size s) const
83 {
84 static_assert(static_cast<std::size_t>(s) <= N, "Out of range");
85 return substr(std::integral_constant<std::size_t, static_cast<std::size_t>(s)>{},
86 std::integral_constant<std::size_t, N - static_cast<std::size_t>(s)>{});
87 }
88
89 template<kumi::concepts::index Size> KUMI_ABI constexpr auto remove_suffix(Size s) const
90 {
91 static_assert(static_cast<std::size_t>(s) <= N, "Out of range");
92 return substr(std::integral_constant<std::size_t, 0>{},
93 std::integral_constant<std::size_t, N - static_cast<std::size_t>(s)>{});
94 }
95
96 template<std::size_t M> KUMI_ABI constexpr bool starts_with(char const (&s)[M]) const
97 {
98 9 constexpr std::size_t n = M - 1;
99 1 if (n > N) return false;
100
10/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 1 times.
28 for (std::size_t i = 0; i < n; ++i)
101
7/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 5 times.
21 if (data_[i] != s[i]) return false;
102 7 return true;
103 }
104
105 template<std::size_t M> KUMI_ABI constexpr bool ends_with(char const (&s)[M]) const
106 {
107 12 constexpr std::size_t n = M - 1;
108 if (n > N) return false;
109
14/20
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
22 for (std::size_t i = 0; i < n; ++i)
110
10/20
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
16 if (data_[N - n + i] != s[i]) return false;
111 6 return true;
112 }
113
114 2 template<std::size_t M> KUMI_ABI constexpr bool contains(char const (&s)[M]) const { return find(s) != kumi::npos; }
115
116 template<std::size_t M> KUMI_ABI constexpr std::size_t find(char const (&s)[M], std::size_t pos = 0) const
117 {
118 9 constexpr std::size_t n = M - 1;
119 if (n == 0) return pos <= N ? pos : kumi::npos;
120 1 if (n > N) return kumi::npos;
121
7/10
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
31 for (std::size_t i = pos; i <= N - n; ++i)
122 {
123 29 bool match = true;
124
10/10
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 1 times.
39 for (std::size_t j = 0; j < n; ++j)
125
10/10
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
33 if (data_[i + j] != s[j])
126 {
127 23 match = false;
128 23 break;
129 }
130
10/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
29 if (match) return i;
131 }
132 2 return kumi::npos;
133 }
134
135 template<std::size_t M> KUMI_ABI constexpr int compare(char const (&other)[M]) const noexcept
136 {
137 25 constexpr std::size_t n = M - 1;
138 25 constexpr std::size_t min_size = (N < n) ? N : n;
139
140
12/14
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 11 times.
✓ Branch 13 taken 1 times.
85 for (std::size_t i = 0; i < min_size; ++i)
141 {
142
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
68 if (data_[i] < other[i]) return -1;
143
10/14
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 11 times.
65 if (data_[i] > other[i]) return 1;
144 }
145 if (N < n) return -1;
146 1 if (N > n) return 1;
147 16 return 0;
148 }
149
150 template<std::size_t M> KUMI_ABI constexpr std::size_t rfind(char const (&s)[M], std::size_t pos = kumi::npos) const
151 {
152 2 constexpr std::size_t n = M - 1;
153 if (n == 0) return (pos > N ? N : pos);
154 if (n > N) return kumi::npos;
155 2 std::size_t start = (pos > N - n) ? (N - n) : pos;
156
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 for (std::size_t i = start; i > 0; --i)
157 {
158 1 bool match = true;
159
2/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 for (std::size_t j = 0; j < n; ++j)
160
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (data_[i + j] != s[j])
161 {
162 ✗ match = false;
163 ✗ break;
164 }
165
1/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (match) return i;
166 }
167 1 return kumi::npos;
168 }
169
170 template<std::size_t M> KUMI_ABI constexpr std::size_t find_first_of(char const (&s)[M], std::size_t pos = 0) const
171 {
172 2 constexpr std::size_t n = M - 1;
173
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
17 for (std::size_t i = pos; i < N; ++i)
174
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 11 times.
31 for (std::size_t j = 0; j < n; ++j)
175
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
16 if (data_[i] == s[j]) return i;
176 1 return kumi::npos;
177 }
178
179 template<std::size_t M>
180 KUMI_ABI constexpr std::size_t find_last_of(char const (&s)[M], std::size_t pos = kumi::npos) const
181 {
182 2 constexpr std::size_t n = M - 1;
183 if (N == 0) return kumi::npos;
184 12 for (std::size_t i = (pos >= N ? N - 1 : pos);; --i)
185 {
186
3/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 11 times.
23 for (std::size_t j = 0; j < n; ++j)
187
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
12 if (data_[i] == s[j]) return i;
188
2/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
11 if (i == 0) break;
189 }
190 1 return kumi::npos;
191 }
192
193 template<std::size_t M>
194 KUMI_ABI constexpr std::size_t find_first_not_of(char const (&s)[M], std::size_t pos = 0) const
195 {
196 2 constexpr std::size_t n = M - 1;
197
3/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
15 for (std::size_t i = pos; i < N; ++i)
198 {
199 14 bool found = false;
200
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
30 for (std::size_t j = 0; j < n; ++j)
201
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 13 times.
29 if (data_[i] == s[j])
202 {
203 13 found = true;
204 13 break;
205 }
206
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
14 if (!found) return i;
207 }
208 1 return kumi::npos;
209 }
210
211 template<std::size_t M>
212 KUMI_ABI constexpr std::size_t find_last_not_of(char const (&s)[M], std::size_t pos = kumi::npos) const
213 {
214 2 constexpr std::size_t n = M - 1;
215 if (N == 0) return kumi::npos;
216 14 for (std::size_t i = (pos >= N ? N - 1 : pos);; --i)
217 {
218 14 bool found = false;
219
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
30 for (std::size_t j = 0; j < n; ++j)
220
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 13 times.
29 if (data_[i] == s[j])
221 {
222 13 found = true;
223 13 break;
224 }
225
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
14 if (!found) return i;
226
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
13 if (i == 0) break;
227 }
228 1 return kumi::npos;
229 }
230
231 // N chars + separator + (M - 1) chars
232 template<std::size_t M> KUMI_ABI constexpr str<N + M> operator+(char const (&other)[M]) const
233 {
234 1 str<N + M> res{};
235
236
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (std::size_t i = 0; i < N; ++i) res.data_[i] = data_[i];
237
238 1 res.data_[N] = separator;
239
240
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (std::size_t i = 0; i < M - 1; ++i) res.data_[N + 1 + i] = other[i];
241
242 1 return res;
243 }
244
245 // str + str: a str is not deduced through its conversion to char array, so it needs its own overload
246 template<std::size_t M> KUMI_ABI constexpr str<N + 1 + M> operator+(str<M> const& other) const
247 {
248 2 return *this + other.data_;
249 }
250 };
251
252 template<std::size_t M> str(char const (&)[M]) -> str<M - 1>;
253
254 template<std::size_t N, std::size_t M>
255 KUMI_ABI constexpr bool operator==(str<N> const& lhs, str<M> const& rhs) noexcept
256 {
257 28 return lhs.compare(rhs.data_) == 0;
258 }
259
260 template<std::size_t N, std::size_t M>
261 KUMI_ABI constexpr auto operator<=>(str<N> const& lhs, str<M> const& rhs) noexcept
262 {
263
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
4 return lhs.compare(rhs.data_) <=> 0;
264 }
265
266 template<std::size_t N, std::size_t M>
267 KUMI_ABI constexpr bool operator==(str<N> const& lhs, char const (&rhs)[M]) noexcept
268 {
269 9 return lhs.compare(rhs) == 0;
270 }
271
272 template<std::size_t N, std::size_t M>
273 KUMI_ABI constexpr auto operator<=>(str<N> const& lhs, char const (&rhs)[M]) noexcept
274 {
275 return lhs.compare(rhs) <=> 0;
276 }
277
278 inline namespace literals
279 {
280 template<kumi::str S> KUMI_ABI constexpr auto operator""_str()
281 {
282 3 return S;
283 }
284 }
285
286 //====================================================================================================================
287 /**
288 @ingroup kumi_types
289 @class unknown
290 @brief Type indicating an identifier was not found in a given kumi::product_type
291 **/
292 //====================================================================================================================
293 struct unknown
294 {
295 static constexpr auto value = kumi::str{"kumi::unknown"};
296 using type = decltype(value);
297
298 constexpr inline operator type() const noexcept { return value; }
299
300 KUMI_ABI friend constexpr auto operator<=>(unknown const&, unknown const&) noexcept = default;
301
302 template<typename CharT, typename Traits>
303 5 friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, unknown const&) noexcept
304 {
305 5 return os << "kumi::unknown";
306 }
307 };
308 }
309