1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
5 #include "cmConfigure.h" // IWYU pragma: keep
10 #include <initializer_list>
14 #include <type_traits>
17 #include <cm/string_view>
18 #include <cmext/string_view>
25 * Trait to convert type T into a String.
26 * Implementations must derive from 'std::true_type'
27 * and define an 'into_string' member that accepts
28 * type T (by value or reference) and returns one of:
30 * - 'std::string' to construct an owned instance.
31 * - 'cm::string_view' to construct a borrowed or null instances.
32 * The buffer from which the view is borrowed must outlive
33 * all copies of the resulting String, e.g. static storage.
34 * - 'cm::String' for already-constructed instances.
37 struct IntoString : std::false_type
42 struct IntoString<T&> : IntoString<T>
47 struct IntoString<T const> : IntoString<T>
52 struct IntoString<T const*> : IntoString<T*>
56 template <typename T, std::string::size_type N>
57 struct IntoString<T const[N]> : IntoString<T[N]>
62 struct IntoString<char*> : std::true_type
64 static String into_string(const char* s);
68 struct IntoString<std::nullptr_t> : std::true_type
70 static string_view into_string(std::nullptr_t) { return string_view(); }
73 template <std::string::size_type N>
74 struct IntoString<char[N]> : std::true_type
76 static std::string into_string(char const (&s)[N])
78 return std::string(s, N - 1);
83 struct IntoString<std::string> : std::true_type
85 static std::string into_string(std::string s) { return s; }
89 struct IntoString<char> : std::true_type
91 static std::string into_string(char const& c) { return std::string(1, c); }
95 * Trait to convert type T into a 'cm::string_view'.
96 * Implementations must derive from 'std::true_type' and
97 * define a 'view' member that accepts type T (by reference)
98 * and returns a 'cm::string_view'.
100 template <typename T>
101 struct AsStringView : std::false_type
105 template <typename T>
106 struct AsStringView<T&> : AsStringView<T>
110 template <typename T>
111 struct AsStringView<T const> : AsStringView<T>
115 template <typename T>
116 struct AsStringView<T const*> : AsStringView<T*>
120 template <typename T, std::string::size_type N>
121 struct AsStringView<T const[N]> : AsStringView<T[N]>
126 struct AsStringView<char*> : std::true_type
128 static string_view view(const char* s) { return s; }
131 template <std::string::size_type N>
132 struct AsStringView<char[N]> : std::true_type
134 static string_view view(char const (&s)[N]) { return string_view(s, N - 1); }
138 struct AsStringView<std::string> : std::true_type
140 static string_view view(std::string const& s) { return s; }
144 struct AsStringView<char> : std::true_type
146 static string_view view(const char& s) { return string_view(&s, 1); }
150 struct AsStringView<string_view> : std::true_type
152 static string_view view(string_view const& s) { return s; }
156 struct AsStringView<static_string_view> : std::true_type
158 static string_view view(static_string_view const& s) { return s; }
162 struct AsStringView<String> : std::true_type
164 static string_view view(String const& s);
170 * A custom string type that holds a view of a string buffer
171 * and optionally shares ownership of the buffer. Instances
172 * may have one of the following states:
174 * - null: views and owns nothing.
175 * Conversion to 'bool' is 'false'.
176 * 'data()' and 'c_str()' return nullptr.
177 * 'size()' returns 0.
178 * 'str()' returns an empty string.
180 * - borrowed: views a string but does not own it. This is used
181 * to bind to static storage (e.g. string literals) or for
182 * temporary instances that do not outlive the borrowed buffer.
183 * Copies and substrings still borrow the original buffer.
184 * Mutation allocates a new internal string and converts to
186 * Conversion to 'bool' is 'true'.
187 * 'c_str()' may internally mutate to the 'owned' state.
188 * 'str()' internally mutates to the 'owned' state.
190 * - owned: views an immutable 'std::string' instance owned internally.
191 * Copies and substrings share ownership of the internal string.
192 * Mutation allocates a new internal string.
193 * Conversion to 'bool' is 'true'.
202 using traits_type = std::string::traits_type;
203 using value_type = string_view::value_type;
204 using pointer = string_view::pointer;
205 using const_pointer = string_view::const_pointer;
206 using reference = string_view::reference;
207 using const_reference = string_view::const_reference;
208 using const_iterator = string_view::const_iterator;
209 using iterator = string_view::const_iterator;
210 using const_reverse_iterator = string_view::const_reverse_iterator;
211 using reverse_iterator = string_view::const_reverse_iterator;
212 using difference_type = string_view::difference_type;
213 using size_type = string_view::size_type;
215 static size_type const npos = string_view::npos;
217 /** Construct a null string. */
220 /** Construct from any type implementing the IntoString trait. */
221 template <typename T,
222 typename = typename std::enable_if<IntoString<T>::value>::type>
224 : String(IntoString<T>::into_string(std::forward<T>(s)), Private())
229 * Construct via static_string_view constructor.
230 * explicit is required to avoid ambiguous overloaded operators (i.e ==,
231 * etc...) with the ones provided by string_view.
233 explicit String(static_string_view s)
234 : String(s, Private())
238 * Construct via string_view constructor.
239 * explicit is required to avoid ambiguous overloaded operators (i.e ==,
240 * etc...) with the ones provided by string_view.
242 explicit String(string_view s)
243 : String(std::string(s), Private())
247 /** Construct via std::string initializer list constructor. */
248 String(std::initializer_list<char> il)
249 : String(std::string(il))
253 /** Construct by copying the specified buffer. */
254 String(const char* d, size_type s)
255 : String(std::string(d, s))
259 /** Construct by copying from input iterator range. */
260 template <typename InputIterator>
261 String(InputIterator first, InputIterator last)
262 : String(std::string(first, last))
266 /** Construct a string with 'n' copies of character 'c'. */
267 String(size_type n, char c)
268 : String(std::string(n, c))
272 /** Construct from a substring of another String instance.
273 This shares ownership of the other string's buffer
274 but views only a substring. */
275 String(String const& s, size_type pos, size_type count = npos)
277 , view_(s.data() + pos, std::min(count, s.size() - pos))
281 /** Construct by moving from another String instance.
282 The other instance is left as a null string. */
283 String(String&& s) noexcept
284 : string_(std::move(s.string_))
287 s.view_ = string_view();
290 /** Construct by copying from another String instance.
291 This shares ownership of the other string's buffer. */
292 String(String const&) noexcept = default;
296 /** Construct by borrowing an externally-owned buffer. The buffer
297 must outlive the returned instance and all copies of it. */
298 static String borrow(string_view v) { return String(v, Private()); }
300 /** Assign by moving from another String instance.
301 The other instance is left as a null string. */
302 String& operator=(String&& s) noexcept
304 this->string_ = std::move(s.string_);
305 this->view_ = s.view_;
306 s.view_ = string_view();
310 /** Assign by copying from another String instance.
311 This shares ownership of the other string's buffer. */
312 String& operator=(String const&) noexcept = default;
314 String& operator=(static_string_view s)
319 String& operator=(string_view s)
325 /** Assign from any type implementing the IntoString trait. */
326 template <typename T>
327 typename // NOLINT(*)
328 std::enable_if<IntoString<T>::value, String&>::type
331 *this = String(std::forward<T>(s));
335 /** Assign via std::string initializer list constructor. */
336 String& operator=(std::initializer_list<char> il)
342 /** Return true if the instance is not a null string. */
343 explicit operator bool() const noexcept { return this->data() != nullptr; }
345 /** Return a view of the string. */
346 string_view view() const noexcept { return this->view_; }
347 operator string_view() const noexcept { return this->view(); }
349 /** Return true if the instance is an empty stringn or null string. */
350 bool empty() const noexcept { return this->view_.empty(); }
352 /** Return a pointer to the start of the string. */
353 const char* data() const noexcept { return this->view_.data(); }
355 /** Return the length of the string in bytes. */
356 size_type size() const noexcept { return this->view_.size(); }
357 size_type length() const noexcept { return this->view_.length(); }
359 /** Return the character at the given position.
360 No bounds checking is performed. */
361 char operator[](size_type pos) const noexcept { return this->view_[pos]; }
363 /** Return the character at the given position.
364 If the position is out of bounds, throws std::out_of_range. */
365 char at(size_type pos) const { return this->view_.at(pos); }
367 char front() const noexcept { return this->view_.front(); }
369 char back() const noexcept { return this->view_.back(); }
371 /** Return true if this instance is stable and otherwise false.
372 An instance is stable if it is in the 'null' state or if it is
373 an 'owned' state not produced by substring operations, or
374 after a call to 'stabilize()' or 'str()'. */
375 bool is_stable() const;
377 /** If 'is_stable()' does not return true, mutate so it does. */
380 /** Get a pointer to a normal std::string if 'is_stable()' returns
381 true and otherwise nullptr. The pointer is valid until this
382 instance is mutated or destroyed. */
383 std::string const* str_if_stable() const;
385 /** Get a reference to a normal std::string. The reference
386 is valid until this instance is mutated or destroyed. */
387 std::string const& str();
389 /** Get a pointer to a C-style null-terminated string
390 containing the same value as this instance. The pointer
391 is valid until this instance is mutated, destroyed,
392 or str() is called. */
395 const_iterator begin() const noexcept { return this->view_.begin(); }
396 const_iterator end() const noexcept { return this->view_.end(); }
397 const_iterator cbegin() const noexcept { return this->begin(); }
398 const_iterator cend() const noexcept { return this->end(); }
400 const_reverse_iterator rbegin() const noexcept
402 return this->view_.rbegin();
404 const_reverse_iterator rend() const noexcept { return this->view_.rend(); }
405 const_reverse_iterator crbegin() const noexcept { return this->rbegin(); }
406 const_reverse_iterator crend() const noexcept { return this->rend(); }
408 /** Append to the string using any type that implements the
409 AsStringView trait. */
410 template <typename T>
411 typename std::enable_if<AsStringView<T>::value, String&>::type operator+=(
414 string_view v = AsStringView<T>::view(std::forward<T>(s));
416 r.reserve(this->size() + v.size());
417 r.assign(this->data(), this->size());
418 r.append(v.data(), v.size());
419 return *this = std::move(r);
422 /** Assign to an empty string. */
423 void clear() { *this = ""_s; }
425 /** Insert 'count' copies of 'ch' at position 'index'. */
426 String& insert(size_type index, size_type count, char ch);
428 /** Erase 'count' characters starting at position 'index'. */
429 String& erase(size_type index = 0, size_type count = npos);
431 void push_back(char ch)
434 s.reserve(this->size() + 1);
435 s.assign(this->data(), this->size());
437 *this = std::move(s);
440 void pop_back() { *this = String(*this, 0, this->size() - 1); }
442 template <typename T>
443 typename std::enable_if<AsStringView<T>::value, String&>::type replace(
444 size_type pos, size_type count, T&& s)
446 const_iterator first = this->begin() + pos;
447 const_iterator last = first + count;
448 return this->replace(first, last, std::forward<T>(s));
451 template <typename InputIterator>
452 String& replace(const_iterator first, const_iterator last,
453 InputIterator first2, InputIterator last2)
456 out.append(this->view_.begin(), first);
457 out.append(first2, last2);
458 out.append(last, this->view_.end());
459 return *this = std::move(out);
462 template <typename T>
463 typename std::enable_if<AsStringView<T>::value, String&>::type replace(
464 const_iterator first, const_iterator last, T&& s)
466 string_view v = AsStringView<T>::view(std::forward<T>(s));
468 out.reserve((first - this->view_.begin()) + v.size() +
469 (this->view_.end() - last));
470 out.append(this->view_.begin(), first);
471 out.append(v.data(), v.size());
472 out.append(last, this->view_.end());
473 return *this = std::move(out);
476 template <typename T>
477 typename std::enable_if<AsStringView<T>::value, String&>::type replace(
478 size_type pos, size_type count, T&& s, size_type pos2,
479 size_type count2 = npos)
481 string_view v = AsStringView<T>::view(std::forward<T>(s));
482 v = v.substr(pos2, count2);
483 return this->replace(pos, count, v);
486 String& replace(size_type pos, size_type count, size_type count2, char ch)
488 const_iterator first = this->begin() + pos;
489 const_iterator last = first + count;
490 return this->replace(first, last, count2, ch);
493 String& replace(const_iterator first, const_iterator last, size_type count2,
497 out.reserve((first - this->view_.begin()) + count2 +
498 (this->view_.end() - last));
499 out.append(this->view_.begin(), first);
500 out.append(count2, ch);
501 out.append(last, this->view_.end());
502 return *this = std::move(out);
505 size_type copy(char* dest, size_type count, size_type pos = 0) const;
507 void resize(size_type count) { this->resize(count, char()); }
509 void resize(size_type count, char ch)
513 if (count <= this->size()) {
514 s.assign(this->data(), count);
516 s.assign(this->data(), this->size());
519 *this = std::move(s);
522 void swap(String& other)
524 std::swap(this->string_, other.string_);
525 std::swap(this->view_, other.view_);
528 /** Return a substring starting at position 'pos' and
529 consisting of at most 'count' characters. */
530 String substr(size_type pos = 0, size_type count = npos) const;
532 template <typename T>
533 typename std::enable_if<AsStringView<T>::value, int>::type compare(
536 return this->view_.compare(AsStringView<T>::view(std::forward<T>(s)));
539 int compare(size_type pos1, size_type count1, string_view v) const
541 return this->view_.compare(pos1, count1, v);
544 int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
545 size_type count2) const
547 return this->view_.compare(pos1, count1, v, pos2, count2);
550 int compare(size_type pos1, size_type count1, const char* s) const
552 return this->view_.compare(pos1, count1, s);
555 int compare(size_type pos1, size_type count1, const char* s,
556 size_type count2) const
558 return this->view_.compare(pos1, count1, s, count2);
561 template <typename T>
562 typename std::enable_if<AsStringView<T>::value, size_type>::type find(
563 T&& s, size_type pos = 0) const
565 string_view v = AsStringView<T>::view(std::forward<T>(s));
566 return this->view_.find(v, pos);
569 size_type find(const char* s, size_type pos, size_type count) const
571 return this->view_.find(s, pos, count);
574 template <typename T>
575 typename std::enable_if<AsStringView<T>::value, size_type>::type rfind(
576 T&& s, size_type pos = npos) const
578 string_view v = AsStringView<T>::view(std::forward<T>(s));
579 return this->view_.rfind(v, pos);
582 size_type rfind(const char* s, size_type pos, size_type count) const
584 return this->view_.rfind(s, pos, count);
587 template <typename T>
588 typename std::enable_if<AsStringView<T>::value, size_type>::type
589 find_first_of(T&& s, size_type pos = 0) const
591 string_view v = AsStringView<T>::view(std::forward<T>(s));
592 return this->view_.find_first_of(v, pos);
595 size_type find_first_of(const char* s, size_type pos, size_type count) const
597 return this->view_.find_first_of(s, pos, count);
600 template <typename T>
601 typename std::enable_if<AsStringView<T>::value, size_type>::type
602 find_first_not_of(T&& s, size_type pos = 0) const
604 string_view v = AsStringView<T>::view(std::forward<T>(s));
605 return this->view_.find_first_not_of(v, pos);
608 size_type find_first_not_of(const char* s, size_type pos,
609 size_type count) const
611 return this->view_.find_first_not_of(s, pos, count);
614 template <typename T>
615 typename std::enable_if<AsStringView<T>::value, size_type>::type
616 find_last_of(T&& s, size_type pos = npos) const
618 string_view v = AsStringView<T>::view(std::forward<T>(s));
619 return this->view_.find_last_of(v, pos);
622 size_type find_last_of(const char* s, size_type pos, size_type count) const
624 return this->view_.find_last_of(s, pos, count);
627 template <typename T>
628 typename std::enable_if<AsStringView<T>::value, size_type>::type
629 find_last_not_of(T&& s, size_type pos = npos) const
631 string_view v = AsStringView<T>::view(std::forward<T>(s));
632 return this->view_.find_last_not_of(v, pos);
635 size_type find_last_not_of(const char* s, size_type pos,
636 size_type count) const
638 return this->view_.find_last_not_of(s, pos, count);
642 // Internal constructor to move from existing String.
643 String(String&& s, Private) noexcept
644 : String(std::move(s))
648 // Internal constructor for dynamically allocated string.
649 String(std::string&& s, Private);
651 // Internal constructor for view of statically allocated string.
652 String(string_view v, Private)
657 void internally_mutate_to_stable_string();
659 std::shared_ptr<std::string const> string_;
664 * Trait for comparable types.
666 template <typename T>
667 struct IsComparable : std::false_type
671 template <typename T>
672 struct IsComparable<T&> : IsComparable<T>
676 template <typename T>
677 struct IsComparable<T const> : IsComparable<T>
681 template <typename T>
682 struct IsComparable<T const*> : IsComparable<T*>
686 template <typename T, std::string::size_type N>
687 struct IsComparable<T const[N]> : IsComparable<T[N]>
692 struct IsComparable<char*> : std::true_type
696 template <std::string::size_type N>
697 struct IsComparable<char[N]> : std::true_type
702 struct IsComparable<std::string> : std::true_type
707 struct IsComparable<char> : std::true_type
711 /** comparison operators */
712 inline bool operator==(const String& l, const String& r)
714 return l.view() == r.view();
716 template <typename L>
717 typename std::enable_if<IsComparable<L>::value, bool>::type operator==(
718 L&& l, const String& r)
720 return AsStringView<L>::view(std::forward<L>(l)) == r.view();
722 template <typename R>
723 typename std::enable_if<IsComparable<R>::value, bool>::type operator==(
724 const String& l, R&& r)
726 return l.view() == AsStringView<R>::view(std::forward<R>(r));
729 inline bool operator!=(const String& l, const String& r)
731 return l.view() != r.view();
733 template <typename L>
734 typename std::enable_if<IsComparable<L>::value, bool>::type operator!=(
735 L&& l, const String& r)
737 return AsStringView<L>::view(std::forward<L>(l)) != r.view();
739 template <typename R>
740 typename std::enable_if<IsComparable<R>::value, bool>::type operator!=(
741 const String& l, R&& r)
743 return l.view() != AsStringView<R>::view(std::forward<R>(r));
746 inline bool operator<(const String& l, const String& r)
748 return l.view() < r.view();
750 template <typename L>
751 typename std::enable_if<IsComparable<L>::value, bool>::type operator<(
752 L&& l, const String& r)
754 return AsStringView<L>::view(std::forward<L>(l)) < r.view();
756 template <typename R>
757 typename std::enable_if<IsComparable<R>::value, bool>::type operator<(
758 const String& l, R&& r)
760 return l.view() < AsStringView<R>::view(std::forward<R>(r));
763 inline bool operator<=(const String& l, const String& r)
765 return l.view() <= r.view();
767 template <typename L>
768 typename std::enable_if<IsComparable<L>::value, bool>::type operator<=(
769 L&& l, const String& r)
771 return AsStringView<L>::view(std::forward<L>(l)) <= r.view();
773 template <typename R>
774 typename std::enable_if<IsComparable<R>::value, bool>::type operator<=(
775 const String& l, R&& r)
777 return l.view() <= AsStringView<R>::view(std::forward<R>(r));
780 inline bool operator>(const String& l, const String& r)
782 return l.view() > r.view();
784 template <typename L>
785 typename std::enable_if<IsComparable<L>::value, bool>::type operator>(
786 L&& l, const String& r)
788 return AsStringView<L>::view(std::forward<L>(l)) > r.view();
790 template <typename R>
791 typename std::enable_if<IsComparable<R>::value, bool>::type operator>(
792 const String& l, R&& r)
794 return l.view() > AsStringView<R>::view(std::forward<R>(r));
797 inline bool operator>=(const String& l, const String& r)
799 return l.view() >= r.view();
801 template <typename L>
802 typename std::enable_if<IsComparable<L>::value, bool>::type operator>=(
803 L&& l, const String& r)
805 return AsStringView<L>::view(std::forward<L>(l)) >= r.view();
807 template <typename R>
808 typename std::enable_if<IsComparable<R>::value, bool>::type operator>=(
809 const String& l, R&& r)
811 return l.view() >= AsStringView<R>::view(std::forward<R>(r));
814 std::ostream& operator<<(std::ostream& os, String const& s);
815 std::string& operator+=(std::string& self, String const& s);
817 template <typename L, typename R>
822 #if defined(__SUNPRO_CC)
823 StringOpPlus(L in_l, R in_r)
829 operator std::string() const;
830 std::string::size_type size() const
832 return this->l.size() + this->r.size();
836 template <typename T>
839 static const bool value = AsStringView<T>::value;
840 using temp_type = string_view;
841 template <typename S>
842 static temp_type temp(S&& s)
844 return AsStringView<T>::view(std::forward<S>(s));
848 template <typename L, typename R>
849 struct StringAdd<StringOpPlus<L, R>> : std::true_type
851 using temp_type = StringOpPlus<L, R> const&;
852 static temp_type temp(temp_type s) { return s; }
855 template <typename L, typename R>
856 StringOpPlus<L, R>::operator std::string() const
859 s.reserve(this->size());
864 template <typename L, typename R>
865 std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a)
867 s.reserve(s.size() + a.size());
873 template <typename L, typename R>
874 String& operator+=(String& s, StringOpPlus<L, R> const& a)
877 r.reserve(s.size() + a.size());
878 r.assign(s.data(), s.size());
885 template <typename L, typename R>
886 std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a)
888 return os << a.l << a.r;
891 template <typename L, typename R>
892 struct IntoString<StringOpPlus<L, R>> : std::true_type
894 static std::string into_string(StringOpPlus<L, R> const& a) { return a; }
897 template <typename L, typename R>
898 typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value,
899 StringOpPlus<typename StringAdd<L>::temp_type,
900 typename StringAdd<R>::temp_type>>::type
901 operator+(L&& l, R&& r)
903 return { StringAdd<L>::temp(std::forward<L>(l)),
904 StringAdd<R>::temp(std::forward<R>(r)) };
907 template <typename LL, typename LR, typename R>
908 typename std::enable_if<AsStringView<R>::value, bool>::type operator==(
909 StringOpPlus<LL, LR> const& l, R&& r)
911 return std::string(l) == AsStringView<R>::view(std::forward<R>(r));
914 template <typename L, typename RL, typename RR>
915 typename std::enable_if<AsStringView<L>::value, bool>::type operator==(
916 L&& l, StringOpPlus<RL, RR> const& r)
918 return AsStringView<L>::view(std::forward<L>(l)) == std::string(r);
926 struct hash<cm::String>
928 using argument_type = cm::String;
929 using result_type = size_t;
931 result_type operator()(argument_type const& s) const noexcept
933 result_type const h(std::hash<cm::string_view>{}(s.view()));