| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #ifndef cmString_hxx
- #define cmString_hxx
- #include "cmConfigure.h" // IWYU pragma: keep
- #include "cm_static_string_view.hxx"
- #include "cm_string_view.hxx"
- #include <algorithm>
- #include <functional>
- #include <initializer_list>
- #include <memory>
- #include <ostream>
- #include <string>
- #include <type_traits>
- namespace cm {
- class String;
- /**
- * Trait to convert type T into a String.
- * Implementations must derive from 'std::true_type'
- * and define an 'into_string' member that accepts
- * type T (by value or reference) and returns one of:
- *
- * - 'std::string' to construct an owned instance.
- * - 'cm::string_view' to construct a borrowed or null instances.
- * The buffer from which the view is borrowed must outlive
- * all copies of the resulting String, e.g. static storage.
- * - 'cm::String' for already-constructed instances.
- */
- template <typename T>
- struct IntoString : std::false_type
- {
- };
- template <typename T>
- struct IntoString<T&> : IntoString<T>
- {
- };
- template <typename T>
- struct IntoString<T const> : IntoString<T>
- {
- };
- template <typename T>
- struct IntoString<T const*> : IntoString<T*>
- {
- };
- template <typename T, std::string::size_type N>
- struct IntoString<T const[N]> : IntoString<T[N]>
- {
- };
- template <>
- struct IntoString<char*> : std::true_type
- {
- static String into_string(const char* s);
- };
- template <>
- struct IntoString<std::nullptr_t> : std::true_type
- {
- static string_view into_string(std::nullptr_t) { return string_view(); }
- };
- template <std::string::size_type N>
- struct IntoString<char[N]> : std::true_type
- {
- static std::string into_string(char const (&s)[N])
- {
- return std::string(s, N - 1);
- }
- };
- template <>
- struct IntoString<std::string> : std::true_type
- {
- static std::string into_string(std::string s) { return s; }
- };
- template <>
- struct IntoString<string_view> : std::true_type
- {
- static std::string into_string(string_view s) { return std::string(s); }
- };
- template <>
- struct IntoString<static_string_view> : std::true_type
- {
- static string_view into_string(static_string_view s) { return s; }
- };
- template <>
- struct IntoString<char> : std::true_type
- {
- static std::string into_string(char const& c) { return std::string(1, c); }
- };
- /**
- * Trait to convert type T into a 'cm::string_view'.
- * Implementations must derive from 'std::true_type' and
- * define a 'view' member that accepts type T (by reference)
- * and returns a 'cm::string_view'.
- */
- template <typename T>
- struct AsStringView : std::false_type
- {
- };
- template <typename T>
- struct AsStringView<T&> : AsStringView<T>
- {
- };
- template <typename T>
- struct AsStringView<T const> : AsStringView<T>
- {
- };
- template <typename T>
- struct AsStringView<T const*> : AsStringView<T*>
- {
- };
- template <typename T, std::string::size_type N>
- struct AsStringView<T const[N]> : AsStringView<T[N]>
- {
- };
- template <>
- struct AsStringView<char*> : std::true_type
- {
- static string_view view(const char* s) { return s; }
- };
- template <std::string::size_type N>
- struct AsStringView<char[N]> : std::true_type
- {
- static string_view view(char const (&s)[N]) { return string_view(s, N - 1); }
- };
- template <>
- struct AsStringView<std::string> : std::true_type
- {
- static string_view view(std::string const& s) { return s; }
- };
- template <>
- struct AsStringView<char> : std::true_type
- {
- static string_view view(const char& s) { return string_view(&s, 1); }
- };
- template <>
- struct AsStringView<string_view> : std::true_type
- {
- static string_view view(string_view const& s) { return s; }
- };
- template <>
- struct AsStringView<static_string_view> : std::true_type
- {
- static string_view view(static_string_view const& s) { return s; }
- };
- template <>
- struct AsStringView<String> : std::true_type
- {
- static string_view view(String const& s);
- };
- /**
- * \class String
- *
- * A custom string type that holds a view of a string buffer
- * and optionally shares ownership of the buffer. Instances
- * may have one of the following states:
- *
- * - null: views and owns nothing.
- * Conversion to 'bool' is 'false'.
- * 'data()' and 'c_str()' return nullptr.
- * 'size()' returns 0.
- * 'str()' returns an empty string.
- *
- * - borrowed: views a string but does not own it. This is used
- * to bind to static storage (e.g. string literals) or for
- * temporary instances that do not outlive the borrowed buffer.
- * Copies and substrings still borrow the original buffer.
- * Mutation allocates a new internal string and converts to
- * the 'owned' state.
- * Conversion to 'bool' is 'true'.
- * 'c_str()' may internally mutate to the 'owned' state.
- * 'str()' internally mutates to the 'owned' state.
- *
- * - owned: views an immutable 'std::string' instance owned internally.
- * Copies and substrings share ownership of the internal string.
- * Mutation allocates a new internal string.
- * Conversion to 'bool' is 'true'.
- */
- class String
- {
- enum class Private
- {
- };
- public:
- using traits_type = std::string::traits_type;
- using value_type = string_view::value_type;
- using pointer = string_view::pointer;
- using const_pointer = string_view::const_pointer;
- using reference = string_view::reference;
- using const_reference = string_view::const_reference;
- using const_iterator = string_view::const_iterator;
- using iterator = string_view::const_iterator;
- using const_reverse_iterator = string_view::const_reverse_iterator;
- using reverse_iterator = string_view::const_reverse_iterator;
- using difference_type = string_view::difference_type;
- using size_type = string_view::size_type;
- static size_type const npos = string_view::npos;
- /** Construct a null string. */
- String() = default;
- /** Construct from any type implementing the IntoString trait. */
- template <typename T,
- typename = typename std::enable_if<IntoString<T>::value>::type>
- String(T&& s)
- : String(IntoString<T>::into_string(std::forward<T>(s)), Private())
- {
- }
- /** Construct via std::string initializer list constructor. */
- String(std::initializer_list<char> il)
- : String(std::string(il))
- {
- }
- /** Construct by copying the specified buffer. */
- String(const char* d, size_type s)
- : String(std::string(d, s))
- {
- }
- /** Construct by copying from input iterator range. */
- template <typename InputIterator>
- String(InputIterator first, InputIterator last)
- : String(std::string(first, last))
- {
- }
- /** Construct a string with 'n' copies of character 'c'. */
- String(size_type n, char c)
- : String(std::string(n, c))
- {
- }
- /** Construct from a substring of another String instance.
- This shares ownership of the other string's buffer
- but views only a substring. */
- String(String const& s, size_type pos, size_type count = npos)
- : string_(s.string_)
- , view_(s.data() + pos, std::min(count, s.size() - pos))
- {
- }
- /** Construct by moving from another String instance.
- The other instance is left as a null string. */
- String(String&& s) noexcept
- : string_(std::move(s.string_))
- , view_(s.view_)
- {
- s.view_ = string_view();
- }
- /** Construct by copying from another String instance.
- This shares ownership of the other string's buffer. */
- String(String const&) noexcept = default;
- ~String() = default;
- /** Construct by borrowing an externally-owned buffer. The buffer
- must outlive the returned instance and all copies of it. */
- static String borrow(string_view v) { return String(v, Private()); }
- /** Assign by moving from another String instance.
- The other instance is left as a null string. */
- String& operator=(String&& s) noexcept
- {
- string_ = std::move(s.string_);
- view_ = s.view_;
- s.view_ = string_view();
- return *this;
- }
- /** Assign by copying from another String instance.
- This shares ownership of the other string's buffer. */
- String& operator=(String const&) noexcept = default;
- /** Assign from any type implementing the IntoString trait. */
- template <typename T>
- typename // NOLINT(*)
- std::enable_if<IntoString<T>::value, String&>::type
- operator=(T&& s)
- {
- *this = String(std::forward<T>(s));
- return *this;
- }
- /** Assign via std::string initializer list constructor. */
- String& operator=(std::initializer_list<char> il)
- {
- *this = String(il);
- return *this;
- }
- /** Return true if the instance is not a null string. */
- explicit operator bool() const noexcept { return data() != nullptr; }
- /** Return a view of the string. */
- string_view view() const noexcept { return view_; }
- /** Return true if the instance is an empty stringn or null string. */
- bool empty() const noexcept { return view_.empty(); }
- /** Return a pointer to the start of the string. */
- const char* data() const noexcept { return view_.data(); }
- /** Return the length of the string in bytes. */
- size_type size() const noexcept { return view_.size(); }
- size_type length() const noexcept { return view_.length(); }
- /** Return the character at the given position.
- No bounds checking is performed. */
- char operator[](size_type pos) const noexcept { return view_[pos]; }
- /** Return the character at the given position.
- If the position is out of bounds, throws std::out_of_range. */
- char at(size_type pos) const { return view_.at(pos); }
- char front() const noexcept { return view_.front(); }
- char back() const noexcept { return view_.back(); }
- /** Return true if this instance is stable and otherwise false.
- An instance is stable if it is in the 'null' state or if it is
- an 'owned' state not produced by substring operations, or
- after a call to 'stabilize()' or 'str()'. */
- bool is_stable() const;
- /** If 'is_stable()' does not return true, mutate so it does. */
- void stabilize();
- /** Get a pointer to a normal std::string if 'is_stable()' returns
- true and otherwise nullptr. The pointer is valid until this
- instance is mutated or destroyed. */
- std::string const* str_if_stable() const;
- /** Get a refernce to a normal std::string. The reference
- is valid until this instance is mutated or destroyed. */
- std::string const& str();
- /** Get a pointer to a C-style null-terminated string
- containing the same value as this instance. The pointer
- is valid until this instance is mutated, destroyed,
- or str() is called. */
- const char* c_str();
- const_iterator begin() const noexcept { return view_.begin(); }
- const_iterator end() const noexcept { return view_.end(); }
- const_iterator cbegin() const noexcept { return begin(); }
- const_iterator cend() const noexcept { return end(); }
- const_reverse_iterator rbegin() const noexcept { return view_.rbegin(); }
- const_reverse_iterator rend() const noexcept { return view_.rend(); }
- const_reverse_iterator crbegin() const noexcept { return rbegin(); }
- const_reverse_iterator crend() const noexcept { return rend(); }
- /** Append to the string using any type that implements the
- AsStringView trait. */
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, String&>::type operator+=(
- T&& s)
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- std::string r;
- r.reserve(size() + v.size());
- r.assign(data(), size());
- r.append(v.data(), v.size());
- return *this = std::move(r);
- }
- /** Assign to an empty string. */
- void clear() { *this = ""_s; }
- /** Insert 'count' copies of 'ch' at position 'index'. */
- String& insert(size_type index, size_type count, char ch);
- /** Erase 'count' characters starting at position 'index'. */
- String& erase(size_type index = 0, size_type count = npos);
- void push_back(char ch)
- {
- std::string s;
- s.reserve(size() + 1);
- s.assign(data(), size());
- s.push_back(ch);
- *this = std::move(s);
- }
- void pop_back() { *this = String(*this, 0, size() - 1); }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, String&>::type replace(
- size_type pos, size_type count, T&& s)
- {
- const_iterator first = begin() + pos;
- const_iterator last = first + count;
- return replace(first, last, std::forward<T>(s));
- }
- template <typename InputIterator>
- String& replace(const_iterator first, const_iterator last,
- InputIterator first2, InputIterator last2)
- {
- std::string out;
- out.append(view_.begin(), first);
- out.append(first2, last2);
- out.append(last, view_.end());
- return *this = std::move(out);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, String&>::type replace(
- const_iterator first, const_iterator last, T&& s)
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- std::string out;
- out.reserve((first - view_.begin()) + v.size() + (view_.end() - last));
- out.append(view_.begin(), first);
- out.append(v.data(), v.size());
- out.append(last, view_.end());
- return *this = std::move(out);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, String&>::type replace(
- size_type pos, size_type count, T&& s, size_type pos2,
- size_type count2 = npos)
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- v = v.substr(pos2, count2);
- return replace(pos, count, v);
- }
- String& replace(size_type pos, size_type count, size_type count2, char ch)
- {
- const_iterator first = begin() + pos;
- const_iterator last = first + count;
- return replace(first, last, count2, ch);
- }
- String& replace(const_iterator first, const_iterator last, size_type count2,
- char ch)
- {
- std::string out;
- out.reserve((first - view_.begin()) + count2 + (view_.end() - last));
- out.append(view_.begin(), first);
- out.append(count2, ch);
- out.append(last, view_.end());
- return *this = std::move(out);
- }
- size_type copy(char* dest, size_type count, size_type pos = 0) const;
- void resize(size_type count) { resize(count, char()); }
- void resize(size_type count, char ch)
- {
- std::string s;
- s.reserve(count);
- if (count <= size()) {
- s.assign(data(), count);
- } else {
- s.assign(data(), size());
- s.resize(count, ch);
- }
- *this = std::move(s);
- }
- void swap(String& other)
- {
- std::swap(string_, other.string_);
- std::swap(view_, other.view_);
- }
- /** Return a substring starting at position 'pos' and
- consisting of at most 'count' characters. */
- String substr(size_type pos = 0, size_type count = npos) const;
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, int>::type compare(
- T&& s) const
- {
- return view_.compare(AsStringView<T>::view(std::forward<T>(s)));
- }
- int compare(size_type pos1, size_type count1, string_view v) const
- {
- return view_.compare(pos1, count1, v);
- }
- int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
- size_type count2) const
- {
- return view_.compare(pos1, count1, v, pos2, count2);
- }
- int compare(size_type pos1, size_type count1, const char* s) const
- {
- return view_.compare(pos1, count1, s);
- }
- int compare(size_type pos1, size_type count1, const char* s,
- size_type count2) const
- {
- return view_.compare(pos1, count1, s, count2);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type find(
- T&& s, size_type pos = 0) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.find(v, pos);
- }
- size_type find(const char* s, size_type pos, size_type count) const
- {
- return view_.find(s, pos, count);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type rfind(
- T&& s, size_type pos = npos) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.rfind(v, pos);
- }
- size_type rfind(const char* s, size_type pos, size_type count) const
- {
- return view_.rfind(s, pos, count);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type
- find_first_of(T&& s, size_type pos = 0) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.find_first_of(v, pos);
- }
- size_type find_first_of(const char* s, size_type pos, size_type count) const
- {
- return view_.find_first_of(s, pos, count);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type
- find_first_not_of(T&& s, size_type pos = 0) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.find_first_not_of(v, pos);
- }
- size_type find_first_not_of(const char* s, size_type pos,
- size_type count) const
- {
- return view_.find_first_not_of(s, pos, count);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type
- find_last_of(T&& s, size_type pos = npos) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.find_last_of(v, pos);
- }
- size_type find_last_of(const char* s, size_type pos, size_type count) const
- {
- return view_.find_last_of(s, pos, count);
- }
- template <typename T>
- typename std::enable_if<AsStringView<T>::value, size_type>::type
- find_last_not_of(T&& s, size_type pos = npos) const
- {
- string_view v = AsStringView<T>::view(std::forward<T>(s));
- return view_.find_last_not_of(v, pos);
- }
- size_type find_last_not_of(const char* s, size_type pos,
- size_type count) const
- {
- return view_.find_last_not_of(s, pos, count);
- }
- private:
- // Internal constructor to move from existing String.
- String(String&& s, Private) noexcept
- : String(std::move(s))
- {
- }
- // Internal constructor for dynamically allocated string.
- String(std::string&& s, Private);
- // Internal constructor for view of statically allocated string.
- String(string_view v, Private)
- : view_(v)
- {
- }
- void internally_mutate_to_stable_string();
- std::shared_ptr<std::string const> string_;
- string_view view_;
- };
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator==(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) ==
- AsStringView<R>::view(std::forward<R>(r)));
- }
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator!=(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) !=
- AsStringView<R>::view(std::forward<R>(r)));
- }
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator<(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) <
- AsStringView<R>::view(std::forward<R>(r)));
- }
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator<=(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) <=
- AsStringView<R>::view(std::forward<R>(r)));
- }
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator>(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) >
- AsStringView<R>::view(std::forward<R>(r)));
- }
- template <typename L, typename R>
- typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value,
- bool>::type
- operator>=(L&& l, R&& r)
- {
- return (AsStringView<L>::view(std::forward<L>(l)) >=
- AsStringView<R>::view(std::forward<R>(r)));
- }
- std::ostream& operator<<(std::ostream& os, String const& s);
- std::string& operator+=(std::string& self, String const& s);
- template <typename L, typename R>
- struct StringOpPlus
- {
- L l;
- R r;
- #if defined(__SUNPRO_CC)
- StringOpPlus(L in_l, R in_r)
- : l(in_l)
- , r(in_r)
- {
- }
- #endif
- operator std::string() const;
- std::string::size_type size() const { return l.size() + r.size(); }
- };
- template <typename T>
- struct StringAdd
- {
- static const bool value = AsStringView<T>::value;
- typedef string_view temp_type;
- template <typename S>
- static temp_type temp(S&& s)
- {
- return AsStringView<T>::view(std::forward<S>(s));
- }
- };
- template <typename L, typename R>
- struct StringAdd<StringOpPlus<L, R>> : std::true_type
- {
- typedef StringOpPlus<L, R> const& temp_type;
- static temp_type temp(temp_type s) { return s; }
- };
- template <typename L, typename R>
- StringOpPlus<L, R>::operator std::string() const
- {
- std::string s;
- s.reserve(size());
- s += *this;
- return s;
- }
- template <typename L, typename R>
- std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a)
- {
- s.reserve(s.size() + a.size());
- s += a.l;
- s += a.r;
- return s;
- }
- template <typename L, typename R>
- String& operator+=(String& s, StringOpPlus<L, R> const& a)
- {
- std::string r;
- r.reserve(s.size() + a.size());
- r.assign(s.data(), s.size());
- r += a.l;
- r += a.r;
- s = std::move(r);
- return s;
- }
- template <typename L, typename R>
- std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a)
- {
- return os << a.l << a.r;
- }
- template <typename L, typename R>
- struct IntoString<StringOpPlus<L, R>> : std::true_type
- {
- static std::string into_string(StringOpPlus<L, R> const& a) { return a; }
- };
- template <typename L, typename R>
- typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value,
- StringOpPlus<typename StringAdd<L>::temp_type,
- typename StringAdd<R>::temp_type>>::type
- operator+(L&& l, R&& r)
- {
- return { StringAdd<L>::temp(std::forward<L>(l)),
- StringAdd<R>::temp(std::forward<R>(r)) };
- }
- template <typename LL, typename LR, typename R>
- typename std::enable_if<AsStringView<R>::value, bool>::type operator==(
- StringOpPlus<LL, LR> const& l, R&& r)
- {
- return std::string(l) == AsStringView<R>::view(std::forward<R>(r));
- }
- template <typename L, typename RL, typename RR>
- typename std::enable_if<AsStringView<L>::value, bool>::type operator==(
- L&& l, StringOpPlus<RL, RR> const& r)
- {
- return AsStringView<L>::view(std::forward<L>(l)) == std::string(r);
- }
- } // namespace cm
- namespace std {
- template <>
- struct hash<cm::String>
- {
- typedef cm::String argument_type;
- typedef size_t result_type;
- result_type operator()(argument_type const& s) const noexcept
- {
- result_type const h(std::hash<cm::string_view>{}(s.view()));
- return h;
- }
- };
- }
- #endif
|