/src/CMake/Source/cmString.hxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #pragma once |
4 | | |
5 | | #include "cmConfigure.h" // IWYU pragma: keep |
6 | | |
7 | | #include <algorithm> |
8 | | #include <cstddef> |
9 | | #include <initializer_list> |
10 | | #include <memory> |
11 | | #include <ostream> |
12 | | #include <string> |
13 | | #include <type_traits> |
14 | | #include <utility> |
15 | | |
16 | | #include <cm/string_view> |
17 | | #include <cmext/string_view> |
18 | | |
19 | | #include <cm3p/rapidhash.h> |
20 | | |
21 | | namespace cm { |
22 | | |
23 | | class String; |
24 | | |
25 | | /** |
26 | | * Trait to convert type T into a String. |
27 | | * Implementations must derive from 'std::true_type' |
28 | | * and define an 'into_string' member that accepts |
29 | | * type T (by value or reference) and returns one of: |
30 | | * |
31 | | * - 'std::string' to construct an owned instance. |
32 | | * - 'cm::string_view' to construct a borrowed or null instances. |
33 | | * The buffer from which the view is borrowed must outlive |
34 | | * all copies of the resulting String, e.g. static storage. |
35 | | * - 'cm::String' for already-constructed instances. |
36 | | */ |
37 | | template <typename T> |
38 | | struct IntoString : std::false_type |
39 | | { |
40 | | }; |
41 | | |
42 | | template <typename T> |
43 | | struct IntoString<T&> : IntoString<T> |
44 | | { |
45 | | }; |
46 | | |
47 | | template <typename T> |
48 | | struct IntoString<T const> : IntoString<T> |
49 | | { |
50 | | }; |
51 | | |
52 | | template <typename T> |
53 | | struct IntoString<T const*> : IntoString<T*> |
54 | | { |
55 | | }; |
56 | | |
57 | | template <typename T, std::string::size_type N> |
58 | | struct IntoString<T const[N]> : IntoString<T[N]> |
59 | | { |
60 | | }; |
61 | | |
62 | | template <> |
63 | | struct IntoString<char*> : std::true_type |
64 | | { |
65 | | static String into_string(char const* s); |
66 | | }; |
67 | | |
68 | | template <> |
69 | | struct IntoString<std::nullptr_t> : std::true_type |
70 | | { |
71 | 0 | static string_view into_string(std::nullptr_t) { return string_view(); } |
72 | | }; |
73 | | |
74 | | template <std::string::size_type N> |
75 | | struct IntoString<char[N]> : std::true_type |
76 | | { |
77 | | static std::string into_string(char const (&s)[N]) |
78 | | { |
79 | | return std::string(s, N - 1); |
80 | | } |
81 | | }; |
82 | | |
83 | | template <> |
84 | | struct IntoString<std::string> : std::true_type |
85 | | { |
86 | 98 | static std::string into_string(std::string s) { return s; } |
87 | | }; |
88 | | |
89 | | template <> |
90 | | struct IntoString<char> : std::true_type |
91 | | { |
92 | 0 | static std::string into_string(char c) { return std::string(1, c); } |
93 | | }; |
94 | | |
95 | | /** |
96 | | * Trait to convert type T into a 'cm::string_view'. |
97 | | * Implementations must derive from 'std::true_type' and |
98 | | * define a 'view' member that accepts type T (by reference) |
99 | | * and returns a 'cm::string_view'. |
100 | | */ |
101 | | template <typename T> |
102 | | struct AsStringView : std::false_type |
103 | | { |
104 | | }; |
105 | | |
106 | | template <typename T> |
107 | | struct AsStringView<T&> : AsStringView<T> |
108 | | { |
109 | | }; |
110 | | |
111 | | template <typename T> |
112 | | struct AsStringView<T const> : AsStringView<T> |
113 | | { |
114 | | }; |
115 | | |
116 | | template <typename T> |
117 | | struct AsStringView<T const*> : AsStringView<T*> |
118 | | { |
119 | | }; |
120 | | |
121 | | template <typename T, std::string::size_type N> |
122 | | struct AsStringView<T const[N]> : AsStringView<T[N]> |
123 | | { |
124 | | }; |
125 | | |
126 | | template <> |
127 | | struct AsStringView<char*> : std::true_type |
128 | | { |
129 | 0 | static string_view view(char const* s) { return s; } |
130 | | }; |
131 | | |
132 | | template <std::string::size_type N> |
133 | | struct AsStringView<char[N]> : std::true_type |
134 | | { |
135 | | static string_view view(char const (&s)[N]) { return string_view(s, N - 1); } |
136 | | }; |
137 | | |
138 | | template <> |
139 | | struct AsStringView<std::string> : std::true_type |
140 | | { |
141 | 0 | static string_view view(std::string const& s) { return s; } |
142 | | }; |
143 | | |
144 | | template <> |
145 | | struct AsStringView<char> : std::true_type |
146 | | { |
147 | | static string_view view( |
148 | | char const& s) // clazy:exclude=function-args-by-value |
149 | 0 | { |
150 | 0 | return string_view(&s, 1); |
151 | 0 | } |
152 | | }; |
153 | | |
154 | | template <> |
155 | | struct AsStringView<string_view> : std::true_type |
156 | | { |
157 | 0 | static string_view view(string_view s) { return s; } |
158 | | }; |
159 | | |
160 | | template <> |
161 | | struct AsStringView<static_string_view> : std::true_type |
162 | | { |
163 | | static string_view view( |
164 | | static_string_view const& s) // clazy:exclude=function-args-by-value |
165 | 0 | { |
166 | 0 | return s; |
167 | 0 | } |
168 | | }; |
169 | | |
170 | | template <> |
171 | | struct AsStringView<String> : std::true_type |
172 | | { |
173 | | static string_view view(String const& s); |
174 | | }; |
175 | | |
176 | | /** |
177 | | * \class String |
178 | | * |
179 | | * A custom string type that holds a view of a string buffer |
180 | | * and optionally shares ownership of the buffer. Instances |
181 | | * may have one of the following states: |
182 | | * |
183 | | * - null: views and owns nothing. |
184 | | * Conversion to 'bool' is 'false'. |
185 | | * 'data()' and 'c_str()' return nullptr. |
186 | | * 'size()' returns 0. |
187 | | * 'str()' returns an empty string. |
188 | | * |
189 | | * - borrowed: views a string but does not own it. This is used |
190 | | * to bind to static storage (e.g. string literals) or for |
191 | | * temporary instances that do not outlive the borrowed buffer. |
192 | | * Copies and substrings still borrow the original buffer. |
193 | | * Mutation allocates a new internal string and converts to |
194 | | * the 'owned' state. |
195 | | * Conversion to 'bool' is 'true'. |
196 | | * 'c_str()' may internally mutate to the 'owned' state. |
197 | | * 'str()' internally mutates to the 'owned' state. |
198 | | * |
199 | | * - owned: views an immutable 'std::string' instance owned internally. |
200 | | * Copies and substrings share ownership of the internal string. |
201 | | * Mutation allocates a new internal string. |
202 | | * Conversion to 'bool' is 'true'. |
203 | | */ |
204 | | class String |
205 | | { |
206 | | enum class Private |
207 | | { |
208 | | }; |
209 | | |
210 | | public: |
211 | | using traits_type = std::string::traits_type; |
212 | | using value_type = string_view::value_type; |
213 | | using pointer = string_view::pointer; |
214 | | using const_pointer = string_view::const_pointer; |
215 | | using reference = string_view::reference; |
216 | | using const_reference = string_view::const_reference; |
217 | | using const_iterator = string_view::const_iterator; |
218 | | using iterator = string_view::const_iterator; |
219 | | using const_reverse_iterator = string_view::const_reverse_iterator; |
220 | | using reverse_iterator = string_view::const_reverse_iterator; |
221 | | using difference_type = string_view::difference_type; |
222 | | using size_type = string_view::size_type; |
223 | | |
224 | | static size_type const npos = string_view::npos; |
225 | | |
226 | | /** Construct a null string. */ |
227 | 95 | String() = default; |
228 | | |
229 | | /** Construct from any type implementing the IntoString trait. */ |
230 | | template <typename T, |
231 | | typename = typename std::enable_if<IntoString<T>::value>::type> |
232 | | String(T&& s) |
233 | 98 | : String(IntoString<T>::into_string(std::forward<T>(s)), Private()) |
234 | 98 | { |
235 | 98 | } Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Unexecuted instantiation: cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) Unexecuted instantiation: cm::String::String<char*, void>(char*&&) cm::String::String<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, void>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 233 | 98 | : String(IntoString<T>::into_string(std::forward<T>(s)), Private()) | 234 | 98 | { | 235 | 98 | } |
|
236 | | |
237 | | /** |
238 | | * Construct via static_string_view constructor. |
239 | | * explicit is required to avoid ambiguous overloaded operators (i.e ==, |
240 | | * etc...) with the ones provided by string_view. |
241 | | */ |
242 | | explicit String(static_string_view s) |
243 | | : String(s, Private()) |
244 | 0 | { |
245 | 0 | } |
246 | | /** |
247 | | * Construct via string_view constructor. |
248 | | * explicit is required to avoid ambiguous overloaded operators (i.e ==, |
249 | | * etc...) with the ones provided by string_view. |
250 | | */ |
251 | | explicit String(string_view s) |
252 | 97 | : String(std::string(s), Private()) |
253 | 97 | { |
254 | 97 | } |
255 | | |
256 | | /** Construct via std::string initializer list constructor. */ |
257 | | String(std::initializer_list<char> il) |
258 | | : String(std::string(il)) |
259 | 0 | { |
260 | 0 | } |
261 | | |
262 | | /** Construct by copying the specified buffer. */ |
263 | | String(char const* d, size_type s) |
264 | 0 | : String(std::string(d, s)) |
265 | 0 | { |
266 | 0 | } |
267 | | |
268 | | /** Construct by copying from input iterator range. */ |
269 | | template <typename InputIterator> |
270 | | String(InputIterator first, InputIterator last) |
271 | | : String(std::string(first, last)) |
272 | | { |
273 | | } |
274 | | |
275 | | /** Construct a string with 'n' copies of character 'c'. */ |
276 | | String(size_type n, char c) |
277 | | : String(std::string(n, c)) |
278 | 0 | { |
279 | 0 | } |
280 | | |
281 | | /** Construct from a substring of another String instance. |
282 | | This shares ownership of the other string's buffer |
283 | | but views only a substring. */ |
284 | | String(String const& s, size_type pos, size_type count = npos) |
285 | 0 | : string_(s.string_) |
286 | 0 | , view_(s.data() + pos, std::min(count, s.size() - pos)) |
287 | 0 | { |
288 | 0 | } |
289 | | |
290 | | /** Construct by moving from another String instance. |
291 | | The other instance is left as a null string. */ |
292 | | String(String&& s) noexcept |
293 | 94 | : string_(std::move(s.string_)) |
294 | 94 | , view_(s.view_) |
295 | 94 | { |
296 | 94 | s.view_ = string_view(); |
297 | 94 | } |
298 | | |
299 | | /** Construct by copying from another String instance. |
300 | | This shares ownership of the other string's buffer. */ |
301 | 0 | String(String const&) noexcept = default; |
302 | | |
303 | 392 | ~String() = default; |
304 | | |
305 | | /** Construct by borrowing an externally-owned buffer. The buffer |
306 | | must outlive the returned instance and all copies of it. */ |
307 | 8 | static String borrow(string_view v) { return String(v, Private()); } |
308 | | |
309 | | /** Assign by moving from another String instance. |
310 | | The other instance is left as a null string. */ |
311 | | String& operator=(String&& s) noexcept |
312 | 98 | { |
313 | 98 | this->string_ = std::move(s.string_); |
314 | 98 | this->view_ = s.view_; |
315 | 98 | s.view_ = string_view(); |
316 | 98 | return *this; |
317 | 98 | } |
318 | | |
319 | | /** Assign by copying from another String instance. |
320 | | This shares ownership of the other string's buffer. */ |
321 | | String& operator=(String const&) noexcept = default; |
322 | | |
323 | | String& operator=(static_string_view s) |
324 | 0 | { |
325 | 0 | *this = String(s); |
326 | 0 | return *this; |
327 | 0 | } |
328 | | String& operator=(string_view s) |
329 | 0 | { |
330 | 0 | *this = String(s); |
331 | 0 | return *this; |
332 | 0 | } |
333 | | |
334 | | /** Assign from any type implementing the IntoString trait. */ |
335 | | template <typename T> |
336 | | typename // NOLINT(*) |
337 | | std::enable_if<IntoString<T>::value, String&>::type |
338 | | operator=(T&& s) |
339 | 0 | { |
340 | 0 | *this = String(std::forward<T>(s)); |
341 | 0 | return *this; |
342 | 0 | } Unexecuted instantiation: _ZN2cm6StringaSINSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSA_ Unexecuted instantiation: _ZN2cm6StringaSIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEENS2_9enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOSB_ Unexecuted instantiation: _ZN2cm6StringaSIPcEENSt3__19enable_ifIXsr10IntoStringIT_EE5valueERS0_E4typeEOS5_ |
343 | | |
344 | | /** Assign via std::string initializer list constructor. */ |
345 | | String& operator=(std::initializer_list<char> il) |
346 | 0 | { |
347 | 0 | *this = String(il); |
348 | 0 | return *this; |
349 | 0 | } |
350 | | |
351 | | /** Return true if the instance is not a null string. */ |
352 | 8 | explicit operator bool() const noexcept { return this->data() != nullptr; } |
353 | | |
354 | | /** Return a view of the string. */ |
355 | 126 | string_view view() const noexcept { return this->view_; } |
356 | 0 | operator string_view() const noexcept { return this->view(); } |
357 | | |
358 | | /** Return true if the instance is an empty stringn or null string. */ |
359 | 0 | bool empty() const noexcept { return this->view_.empty(); } |
360 | | |
361 | | /** Return a pointer to the start of the string. */ |
362 | 20 | char const* data() const noexcept { return this->view_.data(); } |
363 | | |
364 | | /** Return the length of the string in bytes. */ |
365 | 6 | size_type size() const noexcept { return this->view_.size(); } |
366 | 0 | size_type length() const noexcept { return this->view_.length(); } |
367 | | |
368 | | /** Return the character at the given position. |
369 | | No bounds checking is performed. */ |
370 | 0 | char operator[](size_type pos) const noexcept { return this->view_[pos]; } |
371 | | |
372 | | /** Return the character at the given position. |
373 | | If the position is out of bounds, throws std::out_of_range. */ |
374 | 0 | char at(size_type pos) const { return this->view_.at(pos); } |
375 | | |
376 | 0 | char front() const noexcept { return this->view_.front(); } |
377 | | |
378 | 0 | char back() const noexcept { return this->view_.back(); } |
379 | | |
380 | | /** Return true if this instance is stable and otherwise false. |
381 | | An instance is stable if it is in the 'null' state or if it is |
382 | | an 'owned' state not produced by substring operations, or |
383 | | after a call to 'stabilize()' or 'str()'. */ |
384 | | bool is_stable() const; |
385 | | |
386 | | /** If 'is_stable()' does not return true, mutate so it does. */ |
387 | | void stabilize(); |
388 | | |
389 | | /** Get a pointer to a normal std::string if 'is_stable()' returns |
390 | | true and otherwise nullptr. The pointer is valid until this |
391 | | instance is mutated or destroyed. */ |
392 | | std::string const* str_if_stable() const; |
393 | | |
394 | | /** Get a reference to a normal std::string. The reference |
395 | | is valid until this instance is mutated or destroyed. */ |
396 | | std::string const& str(); |
397 | | |
398 | | /** Get a pointer to a C-style null-terminated string |
399 | | containing the same value as this instance. The pointer |
400 | | is valid until this instance is mutated, destroyed, |
401 | | or str() is called. */ |
402 | | char const* c_str(); |
403 | | |
404 | 0 | const_iterator begin() const noexcept { return this->view_.begin(); } |
405 | 0 | const_iterator end() const noexcept { return this->view_.end(); } |
406 | 0 | const_iterator cbegin() const noexcept { return this->begin(); } |
407 | 0 | const_iterator cend() const noexcept { return this->end(); } |
408 | | |
409 | | const_reverse_iterator rbegin() const noexcept |
410 | 0 | { |
411 | 0 | return this->view_.rbegin(); |
412 | 0 | } |
413 | 0 | const_reverse_iterator rend() const noexcept { return this->view_.rend(); } |
414 | 0 | const_reverse_iterator crbegin() const noexcept { return this->rbegin(); } |
415 | 0 | const_reverse_iterator crend() const noexcept { return this->rend(); } |
416 | | |
417 | | /** Append to the string using any type that implements the |
418 | | AsStringView trait. */ |
419 | | template <typename T> |
420 | | typename std::enable_if<AsStringView<T>::value, String&>::type operator+=( |
421 | | T&& s) |
422 | | { |
423 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
424 | | std::string r; |
425 | | r.reserve(this->size() + v.size()); |
426 | | r.assign(this->data(), this->size()); |
427 | | r.append(v.data(), v.size()); |
428 | | return *this = std::move(r); |
429 | | } |
430 | | template <typename T> |
431 | | typename std::enable_if<AsStringView<T>::value, String&>::type append(T&& s) |
432 | 0 | { |
433 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
434 | 0 | std::string r; |
435 | 0 | r.reserve(this->size() + v.size()); |
436 | 0 | r.assign(this->data(), this->size()); |
437 | 0 | r.append(v.data(), v.size()); |
438 | 0 | return *this = std::move(r); |
439 | 0 | } |
440 | | |
441 | | /** Assign to an empty string. */ |
442 | 0 | void clear() { *this = ""_s; } |
443 | | |
444 | | /** Insert 'count' copies of 'ch' at position 'index'. */ |
445 | | String& insert(size_type index, size_type count, char ch); |
446 | | |
447 | | /** Insert into the string using any type that implements the |
448 | | AsStringView trait. */ |
449 | | template <typename T> |
450 | | typename std::enable_if<AsStringView<T>::value, String&>::type insert( |
451 | | size_type index, T&& s) |
452 | 0 | { |
453 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
454 | 0 | std::string r; |
455 | 0 | r.reserve(this->size() + v.size()); |
456 | 0 | r.assign(this->data(), this->size()); |
457 | 0 | r.insert(index, v.data(), v.size()); |
458 | 0 | return *this = std::move(r); |
459 | 0 | } |
460 | | |
461 | | /** Erase 'count' characters starting at position 'index'. */ |
462 | | String& erase(size_type index = 0, size_type count = npos); |
463 | | |
464 | | void push_back(char ch) |
465 | 0 | { |
466 | 0 | std::string s; |
467 | 0 | s.reserve(this->size() + 1); |
468 | 0 | s.assign(this->data(), this->size()); |
469 | 0 | s.push_back(ch); |
470 | 0 | *this = std::move(s); |
471 | 0 | } |
472 | | |
473 | 0 | void pop_back() { *this = String(*this, 0, this->size() - 1); } |
474 | | |
475 | | template <typename T> |
476 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
477 | | size_type pos, size_type count, T&& s) |
478 | | { |
479 | | const_iterator first = this->begin() + pos; |
480 | | const_iterator last = first + count; |
481 | | return this->replace(first, last, std::forward<T>(s)); |
482 | | } |
483 | | |
484 | | template <typename InputIterator> |
485 | | String& replace(const_iterator first, const_iterator last, |
486 | | InputIterator first2, InputIterator last2) |
487 | | { |
488 | | std::string out; |
489 | | out.append(this->view_.begin(), first); |
490 | | out.append(first2, last2); |
491 | | out.append(last, this->view_.end()); |
492 | | return *this = std::move(out); |
493 | | } |
494 | | |
495 | | template <typename T> |
496 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
497 | | const_iterator first, const_iterator last, T&& s) |
498 | | { |
499 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
500 | | std::string out; |
501 | | out.reserve((first - this->view_.begin()) + v.size() + |
502 | | (this->view_.end() - last)); |
503 | | out.append(this->view_.begin(), first); |
504 | | out.append(v.data(), v.size()); |
505 | | out.append(last, this->view_.end()); |
506 | | return *this = std::move(out); |
507 | | } |
508 | | |
509 | | template <typename T> |
510 | | typename std::enable_if<AsStringView<T>::value, String&>::type replace( |
511 | | size_type pos, size_type count, T&& s, size_type pos2, |
512 | | size_type count2 = npos) |
513 | | { |
514 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
515 | | v = v.substr(pos2, count2); |
516 | | return this->replace(pos, count, v); |
517 | | } |
518 | | |
519 | | String& replace(size_type pos, size_type count, size_type count2, char ch) |
520 | 0 | { |
521 | 0 | const_iterator first = this->begin() + pos; |
522 | 0 | const_iterator last = first + count; |
523 | 0 | return this->replace(first, last, count2, ch); |
524 | 0 | } |
525 | | |
526 | | String& replace(const_iterator first, const_iterator last, size_type count2, |
527 | | char ch) |
528 | 0 | { |
529 | 0 | std::string out; |
530 | 0 | out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 + |
531 | 0 | static_cast<size_type>(this->view_.end() - last)); |
532 | 0 | out.append(this->view_.begin(), first); |
533 | 0 | out.append(count2, ch); |
534 | 0 | out.append(last, this->view_.end()); |
535 | 0 | return *this = std::move(out); |
536 | 0 | } |
537 | | |
538 | | size_type copy(char* dest, size_type count, size_type pos = 0) const; |
539 | | |
540 | 0 | void resize(size_type count) { this->resize(count, char()); } |
541 | | |
542 | | void resize(size_type count, char ch) |
543 | 0 | { |
544 | 0 | std::string s; |
545 | 0 | s.reserve(count); |
546 | 0 | if (count <= this->size()) { |
547 | 0 | s.assign(this->data(), count); |
548 | 0 | } else { |
549 | 0 | s.assign(this->data(), this->size()); |
550 | 0 | s.resize(count, ch); |
551 | 0 | } |
552 | 0 | *this = std::move(s); |
553 | 0 | } |
554 | | |
555 | | void swap(String& other) noexcept |
556 | 0 | { |
557 | 0 | std::swap(this->string_, other.string_); |
558 | 0 | std::swap(this->view_, other.view_); |
559 | 0 | } |
560 | | |
561 | | /** Return a substring starting at position 'pos' and |
562 | | consisting of at most 'count' characters. */ |
563 | | String substr(size_type pos = 0, size_type count = npos) const; |
564 | | |
565 | | template <typename T> |
566 | | typename std::enable_if<AsStringView<T>::value, int>::type compare( |
567 | | T&& s) const |
568 | | { |
569 | | return this->view_.compare(AsStringView<T>::view(std::forward<T>(s))); |
570 | | } |
571 | | |
572 | | int compare(size_type pos1, size_type count1, string_view v) const |
573 | 0 | { |
574 | 0 | return this->view_.compare(pos1, count1, v); |
575 | 0 | } |
576 | | |
577 | | int compare(size_type pos1, size_type count1, string_view v, size_type pos2, |
578 | | size_type count2) const |
579 | 0 | { |
580 | 0 | return this->view_.compare(pos1, count1, v, pos2, count2); |
581 | 0 | } |
582 | | |
583 | | int compare(size_type pos1, size_type count1, char const* s) const |
584 | 0 | { |
585 | 0 | return this->view_.compare(pos1, count1, s); |
586 | 0 | } |
587 | | |
588 | | int compare(size_type pos1, size_type count1, char const* s, |
589 | | size_type count2) const |
590 | 0 | { |
591 | 0 | return this->view_.compare(pos1, count1, s, count2); |
592 | 0 | } |
593 | | |
594 | | template <typename T> |
595 | | typename std::enable_if<AsStringView<T>::value, size_type>::type find( |
596 | | T&& s, size_type pos = 0) const |
597 | 0 | { |
598 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
599 | 0 | return this->view_.find(v, pos); |
600 | 0 | } |
601 | | |
602 | | size_type find(char const* s, size_type pos, size_type count) const |
603 | 0 | { |
604 | 0 | return this->view_.find(s, pos, count); |
605 | 0 | } |
606 | | |
607 | | template <typename T> |
608 | | typename std::enable_if<AsStringView<T>::value, size_type>::type rfind( |
609 | | T&& s, size_type pos = npos) const |
610 | 0 | { |
611 | 0 | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
612 | 0 | return this->view_.rfind(v, pos); |
613 | 0 | } |
614 | | |
615 | | size_type rfind(char const* s, size_type pos, size_type count) const |
616 | 0 | { |
617 | 0 | return this->view_.rfind(s, pos, count); |
618 | 0 | } |
619 | | |
620 | | template <typename T> |
621 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
622 | | find_first_of(T&& s, size_type pos = 0) const |
623 | | { |
624 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
625 | | return this->view_.find_first_of(v, pos); |
626 | | } |
627 | | |
628 | | size_type find_first_of(char const* s, size_type pos, size_type count) const |
629 | 0 | { |
630 | 0 | return this->view_.find_first_of(s, pos, count); |
631 | 0 | } |
632 | | |
633 | | template <typename T> |
634 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
635 | | find_first_not_of(T&& s, size_type pos = 0) const |
636 | | { |
637 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
638 | | return this->view_.find_first_not_of(v, pos); |
639 | | } |
640 | | |
641 | | size_type find_first_not_of(char const* s, size_type pos, |
642 | | size_type count) const |
643 | 0 | { |
644 | 0 | return this->view_.find_first_not_of(s, pos, count); |
645 | 0 | } |
646 | | |
647 | | template <typename T> |
648 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
649 | | find_last_of(T&& s, size_type pos = npos) const |
650 | | { |
651 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
652 | | return this->view_.find_last_of(v, pos); |
653 | | } |
654 | | |
655 | | size_type find_last_of(char const* s, size_type pos, size_type count) const |
656 | 0 | { |
657 | 0 | return this->view_.find_last_of(s, pos, count); |
658 | 0 | } |
659 | | |
660 | | template <typename T> |
661 | | typename std::enable_if<AsStringView<T>::value, size_type>::type |
662 | | find_last_not_of(T&& s, size_type pos = npos) const |
663 | | { |
664 | | string_view v = AsStringView<T>::view(std::forward<T>(s)); |
665 | | return this->view_.find_last_not_of(v, pos); |
666 | | } |
667 | | |
668 | | size_type find_last_not_of(char const* s, size_type pos, |
669 | | size_type count) const |
670 | 0 | { |
671 | 0 | return this->view_.find_last_not_of(s, pos, count); |
672 | 0 | } |
673 | | |
674 | | private: |
675 | | // Internal constructor to move from existing String. |
676 | | String(String&& s, Private) noexcept |
677 | 0 | : String(std::move(s)) |
678 | 0 | { |
679 | 0 | } |
680 | | |
681 | | // Internal constructor for dynamically allocated string. |
682 | | String(std::string&& s, Private); |
683 | | |
684 | | // Internal constructor for view of statically allocated string. |
685 | | String(string_view v, Private) |
686 | 8 | : view_(v) |
687 | 8 | { |
688 | 8 | } |
689 | | |
690 | | void internally_mutate_to_stable_string(); |
691 | | |
692 | | std::shared_ptr<std::string const> string_; |
693 | | string_view view_; |
694 | | }; |
695 | | |
696 | | /** |
697 | | * Trait for comparable types. |
698 | | */ |
699 | | template <typename T> |
700 | | struct IsComparable : std::false_type |
701 | | { |
702 | | }; |
703 | | |
704 | | template <typename T> |
705 | | struct IsComparable<T&> : IsComparable<T> |
706 | | { |
707 | | }; |
708 | | |
709 | | template <typename T> |
710 | | struct IsComparable<T const> : IsComparable<T> |
711 | | { |
712 | | }; |
713 | | |
714 | | template <typename T> |
715 | | struct IsComparable<T const*> : IsComparable<T*> |
716 | | { |
717 | | }; |
718 | | |
719 | | template <typename T, std::string::size_type N> |
720 | | struct IsComparable<T const[N]> : IsComparable<T[N]> |
721 | | { |
722 | | }; |
723 | | |
724 | | template <> |
725 | | struct IsComparable<char*> : std::true_type |
726 | | { |
727 | | }; |
728 | | |
729 | | template <std::string::size_type N> |
730 | | struct IsComparable<char[N]> : std::true_type |
731 | | { |
732 | | }; |
733 | | |
734 | | template <> |
735 | | struct IsComparable<std::string> : std::true_type |
736 | | { |
737 | | }; |
738 | | |
739 | | template <> |
740 | | struct IsComparable<char> : std::true_type |
741 | | { |
742 | | }; |
743 | | |
744 | | /** comparison operators */ |
745 | | inline bool operator==(String const& l, String const& r) |
746 | 10 | { |
747 | 10 | return l.view() == r.view(); |
748 | 10 | } |
749 | | template <typename L> |
750 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator==( |
751 | | L&& l, String const& r) |
752 | | { |
753 | | return AsStringView<L>::view(std::forward<L>(l)) == r.view(); |
754 | | } |
755 | | template <typename R> |
756 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator==( |
757 | | String const& l, R&& r) |
758 | | { |
759 | | return l.view() == AsStringView<R>::view(std::forward<R>(r)); |
760 | | } |
761 | | |
762 | | inline bool operator!=(String const& l, String const& r) |
763 | 0 | { |
764 | 0 | return l.view() != r.view(); |
765 | 0 | } |
766 | | template <typename L> |
767 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator!=( |
768 | | L&& l, String const& r) |
769 | | { |
770 | | return AsStringView<L>::view(std::forward<L>(l)) != r.view(); |
771 | | } |
772 | | template <typename R> |
773 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator!=( |
774 | | String const& l, R&& r) |
775 | | { |
776 | | return l.view() != AsStringView<R>::view(std::forward<R>(r)); |
777 | | } |
778 | | |
779 | | inline bool operator<(String const& l, String const& r) |
780 | 0 | { |
781 | 0 | return l.view() < r.view(); |
782 | 0 | } |
783 | | template <typename L> |
784 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator<( |
785 | | L&& l, String const& r) |
786 | | { |
787 | | return AsStringView<L>::view(std::forward<L>(l)) < r.view(); |
788 | | } |
789 | | template <typename R> |
790 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator<( |
791 | | String const& l, R&& r) |
792 | | { |
793 | | return l.view() < AsStringView<R>::view(std::forward<R>(r)); |
794 | | } |
795 | | |
796 | | inline bool operator<=(String const& l, String const& r) |
797 | 0 | { |
798 | 0 | return l.view() <= r.view(); |
799 | 0 | } |
800 | | template <typename L> |
801 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator<=( |
802 | | L&& l, String const& r) |
803 | | { |
804 | | return AsStringView<L>::view(std::forward<L>(l)) <= r.view(); |
805 | | } |
806 | | template <typename R> |
807 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator<=( |
808 | | String const& l, R&& r) |
809 | | { |
810 | | return l.view() <= AsStringView<R>::view(std::forward<R>(r)); |
811 | | } |
812 | | |
813 | | inline bool operator>(String const& l, String const& r) |
814 | 0 | { |
815 | 0 | return l.view() > r.view(); |
816 | 0 | } |
817 | | template <typename L> |
818 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator>( |
819 | | L&& l, String const& r) |
820 | | { |
821 | | return AsStringView<L>::view(std::forward<L>(l)) > r.view(); |
822 | | } |
823 | | template <typename R> |
824 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator>( |
825 | | String const& l, R&& r) |
826 | | { |
827 | | return l.view() > AsStringView<R>::view(std::forward<R>(r)); |
828 | | } |
829 | | |
830 | | inline bool operator>=(String const& l, String const& r) |
831 | 0 | { |
832 | 0 | return l.view() >= r.view(); |
833 | 0 | } |
834 | | template <typename L> |
835 | | typename std::enable_if<IsComparable<L>::value, bool>::type operator>=( |
836 | | L&& l, String const& r) |
837 | | { |
838 | | return AsStringView<L>::view(std::forward<L>(l)) >= r.view(); |
839 | | } |
840 | | template <typename R> |
841 | | typename std::enable_if<IsComparable<R>::value, bool>::type operator>=( |
842 | | String const& l, R&& r) |
843 | | { |
844 | | return l.view() >= AsStringView<R>::view(std::forward<R>(r)); |
845 | | } |
846 | | |
847 | | std::ostream& operator<<(std::ostream& os, String const& s); |
848 | | std::string& operator+=(std::string& self, String const& s); |
849 | | |
850 | | template <typename L, typename R> |
851 | | struct StringOpPlus |
852 | | { |
853 | | L l; |
854 | | R r; |
855 | | #if defined(__SUNPRO_CC) |
856 | | StringOpPlus(L in_l, R in_r) |
857 | | : l(in_l) |
858 | | , r(in_r) |
859 | | { |
860 | | } |
861 | | #endif |
862 | | operator std::string() const; |
863 | | std::string::size_type size() const |
864 | | { |
865 | | return this->l.size() + this->r.size(); |
866 | | } |
867 | | }; |
868 | | |
869 | | template <typename T> |
870 | | struct StringAdd |
871 | | { |
872 | | static bool const value = AsStringView<T>::value; |
873 | | using temp_type = string_view; |
874 | | template <typename S> |
875 | | static temp_type temp(S&& s) |
876 | | { |
877 | | return AsStringView<T>::view(std::forward<S>(s)); |
878 | | } |
879 | | }; |
880 | | |
881 | | template <typename L, typename R> |
882 | | struct StringAdd<StringOpPlus<L, R>> : std::true_type |
883 | | { |
884 | | using temp_type = StringOpPlus<L, R> const&; |
885 | | static temp_type temp(temp_type s) { return s; } |
886 | | }; |
887 | | |
888 | | template <typename L, typename R> |
889 | | StringOpPlus<L, R>::operator std::string() const |
890 | | { |
891 | | std::string s; |
892 | | s.reserve(this->size()); |
893 | | s += *this; |
894 | | return s; |
895 | | } |
896 | | |
897 | | template <typename L, typename R> |
898 | | std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a) |
899 | | { |
900 | | s.reserve(s.size() + a.size()); |
901 | | s += a.l; |
902 | | s += a.r; |
903 | | return s; |
904 | | } |
905 | | |
906 | | template <typename L, typename R> |
907 | | String& operator+=(String& s, StringOpPlus<L, R> const& a) |
908 | | { |
909 | | std::string r; |
910 | | r.reserve(s.size() + a.size()); |
911 | | r.assign(s.data(), s.size()); |
912 | | r += a.l; |
913 | | r += a.r; |
914 | | s = std::move(r); |
915 | | return s; |
916 | | } |
917 | | |
918 | | template <typename L, typename R> |
919 | | std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a) |
920 | | { |
921 | | return os << a.l << a.r; |
922 | | } |
923 | | |
924 | | template <typename L, typename R> |
925 | | struct IntoString<StringOpPlus<L, R>> : std::true_type |
926 | | { |
927 | | static std::string into_string(StringOpPlus<L, R> const& a) { return a; } |
928 | | }; |
929 | | |
930 | | template <typename L, typename R> |
931 | | typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value, |
932 | | StringOpPlus<typename StringAdd<L>::temp_type, |
933 | | typename StringAdd<R>::temp_type>>::type |
934 | | operator+(L&& l, R&& r) |
935 | | { |
936 | | return { StringAdd<L>::temp(std::forward<L>(l)), |
937 | | StringAdd<R>::temp(std::forward<R>(r)) }; |
938 | | } |
939 | | |
940 | | template <typename LL, typename LR, typename R> |
941 | | typename std::enable_if<AsStringView<R>::value, bool>::type operator==( |
942 | | StringOpPlus<LL, LR> const& l, R&& r) |
943 | | { |
944 | | return std::string(l) == AsStringView<R>::view(std::forward<R>(r)); |
945 | | } |
946 | | |
947 | | template <typename L, typename RL, typename RR> |
948 | | typename std::enable_if<AsStringView<L>::value, bool>::type operator==( |
949 | | L&& l, StringOpPlus<RL, RR> const& r) |
950 | | { |
951 | | return AsStringView<L>::view(std::forward<L>(l)) == std::string(r); |
952 | | } |
953 | | |
954 | | } // namespace cm |
955 | | |
956 | | namespace std { |
957 | | |
958 | | template <> |
959 | | struct hash<cm::String> |
960 | | { |
961 | | using argument_type = cm::String; |
962 | | using result_type = size_t; |
963 | | |
964 | | result_type operator()(argument_type const& s) const noexcept |
965 | 106 | { |
966 | 106 | cm::string_view const v = s.view(); |
967 | 106 | return static_cast<result_type>(rapidhash(v.data(), v.size())); |
968 | 106 | } |
969 | | }; |
970 | | } |