Commit | Line | Data |
---|---|---|
a6df2497 JG |
1 | /* |
2 | * Copyright (c) 2023 Philippe Proulx <pproulx@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | */ | |
6 | ||
7 | #ifndef LTTNG_C_STRING_VIEW_HPP | |
8 | #define LTTNG_C_STRING_VIEW_HPP | |
9 | ||
10 | #include <common/format.hpp> | |
11 | #include <common/type-traits.hpp> | |
12 | ||
13 | #include <cstddef> | |
14 | #include <cstring> | |
15 | #include <functional> | |
16 | #include <string> | |
17 | ||
18 | namespace lttng { | |
19 | ||
20 | /* | |
21 | * A view on a constant null-terminated C string. | |
22 | */ | |
23 | class c_string_view final { | |
24 | public: | |
25 | /* | |
26 | * Builds an empty view (data() returns `nullptr`). | |
27 | * | |
28 | * Intentionally not explicit. | |
29 | */ | |
30 | constexpr c_string_view() noexcept = default; | |
31 | ||
32 | /* | |
33 | * Builds a view of the C string `str` (may be `nullptr`). | |
34 | * | |
35 | * Intentionally not explicit. | |
36 | */ | |
37 | /* NOLINTBEGIN(google-explicit-constructor) */ | |
38 | constexpr c_string_view(const char *const str) noexcept : _str{ str } | |
39 | { | |
40 | } | |
41 | /* NOLINTEND(google-explicit-constructor) */ | |
42 | ||
43 | /* | |
44 | * Builds a view of the string `str`. | |
45 | */ | |
46 | /* NOLINTBEGIN(google-explicit-constructor) */ | |
47 | c_string_view(const std::string& str) noexcept : _str{ str.c_str() } | |
48 | { | |
49 | } | |
e49a2b52 | 50 | /* NOLINTEND(google-explicit-constructor) */ |
a6df2497 JG |
51 | |
52 | /* | |
53 | * Makes this view view the C string `str` (may be `nullptr`). | |
54 | */ | |
55 | c_string_view& operator=(const char *const str) noexcept | |
56 | { | |
57 | _str = str; | |
58 | return *this; | |
59 | } | |
60 | ||
61 | /* | |
62 | * Viewed null-terminated C string (may be `nullptr`). | |
63 | */ | |
64 | const char *data() const noexcept | |
65 | { | |
66 | return _str; | |
67 | } | |
68 | ||
69 | /* | |
70 | * Alias of data(). | |
71 | */ | |
72 | operator const char *() const noexcept /* NOLINT(google-explicit-constructor) */ | |
73 | { | |
74 | return this->data(); | |
75 | } | |
76 | ||
77 | /* | |
78 | * Evaluate as boolean (false means an empty string). | |
79 | */ | |
80 | operator bool() const noexcept /* NOLINT(google-explicit-constructor) */ | |
81 | { | |
82 | return *this->data(); | |
83 | } | |
84 | ||
85 | /* | |
86 | * Alias of data(). | |
87 | */ | |
88 | const char *operator*() const noexcept | |
89 | { | |
90 | return this->data(); | |
91 | } | |
92 | ||
93 | /* | |
94 | * Alias of data(). | |
95 | * | |
96 | * data() must not return `nullptr`. | |
97 | */ | |
98 | const char *begin() const noexcept | |
99 | { | |
100 | return this->data(); | |
101 | } | |
102 | ||
103 | /* | |
104 | * Pointer to the null character of the viewed C string. | |
105 | * | |
106 | * data() must not return `nullptr`. | |
107 | */ | |
108 | const char *end() const noexcept | |
109 | { | |
110 | return _str + this->len(); | |
111 | } | |
112 | ||
113 | /* | |
114 | * Length of the viewed C string, excluding the null character. | |
115 | * | |
116 | * data() must not return `nullptr`. | |
117 | */ | |
118 | std::size_t len() const noexcept | |
119 | { | |
120 | return std::strlen(_str); | |
121 | } | |
122 | ||
123 | /* | |
124 | * Returns an `std::string` instance containing a copy of the viewed | |
125 | * C string. | |
126 | * | |
127 | * data() must not return `nullptr`. | |
128 | */ | |
129 | std::string str() const | |
130 | { | |
131 | return std::string{ _str }; | |
132 | } | |
133 | ||
134 | /* | |
135 | * Alias of str(). | |
136 | */ | |
137 | operator std::string() const /* NOLINT(google-explicit-constructor) */ | |
138 | { | |
139 | return this->str(); | |
140 | } | |
141 | ||
142 | /* | |
143 | * Returns the character at index `i`. | |
144 | * | |
145 | * `i` must be less than what len() returns. | |
146 | * | |
147 | * data() must not return `nullptr`. | |
148 | */ | |
149 | char operator[](const std::size_t i) const noexcept | |
150 | { | |
151 | return _str[i]; | |
152 | } | |
153 | ||
154 | bool startsWith(const lttng::c_string_view prefix) const noexcept | |
155 | { | |
156 | return std::strncmp(_str, (const char *) prefix, prefix.len()) == 0; | |
157 | } | |
158 | ||
159 | private: | |
160 | const char *_str = nullptr; | |
161 | }; | |
162 | ||
163 | inline const char *format_as(const c_string_view& str) | |
164 | { | |
165 | return str ? *str : "(null)"; | |
166 | } | |
167 | ||
168 | namespace internal { | |
169 | ||
170 | template <typename StrT> | |
171 | const char *as_const_char_ptr(StrT&& val) noexcept | |
172 | { | |
173 | return val.data(); | |
174 | } | |
175 | ||
176 | inline const char *as_const_char_ptr(const char *const val) noexcept | |
177 | { | |
178 | return val; | |
179 | } | |
180 | ||
181 | template <typename StrT> | |
182 | using comparable_with_c_string_view = lttng::traits:: | |
183 | is_one_of<typename std::decay<StrT>::type, c_string_view, std::string, const char *>; | |
184 | ||
185 | } /* namespace internal */ | |
186 | ||
187 | /* | |
188 | * Returns true if `lhs` is equal to `rhs`. | |
189 | * | |
190 | * `LhsT` and `RhsT` may be any of: | |
191 | * | |
192 | * • `const char *` | |
193 | * • `std::string` | |
194 | * • `c_string_view` | |
195 | * | |
196 | * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data. | |
197 | */ | |
198 | template < | |
199 | typename LhsT, | |
200 | typename RhsT, | |
201 | typename = | |
202 | typename std::enable_if<internal::comparable_with_c_string_view<LhsT>::value>::type, | |
203 | typename = | |
204 | typename std::enable_if<internal::comparable_with_c_string_view<RhsT>::value>::type> | |
205 | bool operator==(LhsT&& lhs, RhsT&& rhs) noexcept | |
206 | { | |
207 | const auto raw_lhs = internal::as_const_char_ptr(lhs); | |
208 | const auto raw_rhs = internal::as_const_char_ptr(rhs); | |
209 | ||
210 | return std::strcmp(raw_lhs, raw_rhs) == 0; | |
211 | } | |
212 | ||
213 | /* | |
214 | * Returns true if `lhs` is not equal to `rhs`. | |
215 | * | |
216 | * `LhsT` and `RhsT` may be any of: | |
217 | * | |
218 | * • `const char *` | |
219 | * • `std::string` | |
220 | * • `c_string_view` | |
221 | * | |
222 | * Both `lhs` and `rhs` must not have an underlying `nullptr` raw data. | |
223 | */ | |
224 | template < | |
225 | typename LhsT, | |
226 | typename RhsT, | |
227 | typename = | |
228 | typename std::enable_if<internal::comparable_with_c_string_view<LhsT>::value>::type, | |
229 | typename = | |
230 | typename std::enable_if<internal::comparable_with_c_string_view<RhsT>::value>::type> | |
231 | bool operator!=(LhsT&& lhs, RhsT&& rhs) noexcept | |
232 | { | |
233 | return !(std::forward<LhsT>(lhs) == std::forward<RhsT>(rhs)); | |
234 | } | |
235 | ||
236 | } /* namespace lttng */ | |
237 | ||
238 | /* | |
239 | * Appends `rhs` to `lhs`. | |
240 | */ | |
241 | inline void operator+=(std::string& lhs, lttng::c_string_view rhs) | |
242 | { | |
243 | lhs += rhs.data(); | |
244 | } | |
245 | ||
246 | namespace std { | |
247 | template <> | |
248 | struct hash<lttng::c_string_view> { | |
249 | std::size_t operator()(const lttng::c_string_view& str) const | |
250 | { | |
251 | auto hash_value = std::hash<char>{}('\0'); | |
252 | ||
253 | for (auto character : str) { | |
254 | hash_value ^= std::hash<decltype(character)>{}(character); | |
255 | } | |
256 | ||
257 | return hash_value; | |
258 | } | |
259 | }; | |
260 | } /* namespace std */ | |
261 | ||
262 | #endif /* LTTNG_C_STRING_VIEW_HPP */ |