summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2018-12-13 14:58:30 (GMT)
committerKitware Robot <kwrobot@kitware.com>2018-12-13 14:58:50 (GMT)
commit0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c (patch)
treec29f1443d50156e4c5f62ef149cff8bc27751458 /Source
parentdc90cd68778b374122162cd3a87a7b6d27ed6866 (diff)
parent2d68b2c593b0bc19273e95f98ff29b3d3cff90c7 (diff)
downloadCMake-0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c.zip
CMake-0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c.tar.gz
CMake-0be5ed7128b59c6ac3b508fa89fe793f5cfdf07c.tar.bz2
Merge topic 'custom-string'
2d68b2c593 String: Add str_if_stable() as a const alternative to str() a0841b59bd String: Add support for a ""_s string literal syntax 9d5fe8e96a String: Add 'borrow' member to construct borrowing instances 80802a002c String: Add support for concatenation by operator+ ff69763ca0 String: Add a custom string type 410a3e4b22 Add support for using C++17 string_view or a fallback Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Daniel Pfeifer <daniel@pfeifer-mail.de> Acked-by: Pavel Solodovnikov <hellyeahdominate@gmail.com> Merge-request: !2578
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt5
-rw-r--r--Source/cmString.cxx152
-rw-r--r--Source/cmString.hxx815
-rw-r--r--Source/cm_static_string_view.hxx41
-rw-r--r--Source/cm_string_view.cxx301
-rw-r--r--Source/cm_string_view.hxx217
6 files changed, 1531 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index e86cc2f..91c45bc 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -383,6 +383,9 @@ set(SRCS
cmXMLWriter.h
cmake.cxx
cmake.h
+ cm_string_view.cxx
+ cm_string_view.hxx
+ cm_static_string_view.hxx
cmCommand.cxx
cmCommand.h
@@ -574,6 +577,8 @@ set(SRCS
cmSiteNameCommand.h
cmSourceGroupCommand.cxx
cmSourceGroupCommand.h
+ cmString.cxx
+ cmString.hxx
cmStringReplaceHelper.cxx
cmStringCommand.cxx
cmStringCommand.h
diff --git a/Source/cmString.cxx b/Source/cmString.cxx
new file mode 100644
index 0000000..2a0c125
--- /dev/null
+++ b/Source/cmString.cxx
@@ -0,0 +1,152 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#define _SCL_SECURE_NO_WARNINGS
+
+#include "cmString.hxx"
+
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+namespace cm {
+
+static std::string const empty_string_;
+
+void String::internally_mutate_to_stable_string()
+{
+ // We assume that only one thread mutates this instance at
+ // a time even if we point to a shared string buffer refernced
+ // by other threads.
+ *this = String(data(), size());
+}
+
+bool String::is_stable() const
+{
+ return str_if_stable() != nullptr;
+}
+
+void String::stabilize()
+{
+ if (is_stable()) {
+ return;
+ }
+ this->internally_mutate_to_stable_string();
+}
+
+std::string const* String::str_if_stable() const
+{
+ if (!data()) {
+ // We view no string.
+ // This is stable for the lifetime of our current value.
+ return &empty_string_;
+ }
+
+ if (string_ && data() == string_->data() && size() == string_->size()) {
+ // We view an entire string.
+ // This is stable for the lifetime of our current value.
+ return string_.get();
+ }
+
+ return nullptr;
+}
+
+std::string const& String::str()
+{
+ if (std::string const* s = str_if_stable()) {
+ return *s;
+ }
+ // Mutate to hold a std::string that is stable for the lifetime
+ // of our current value.
+ this->internally_mutate_to_stable_string();
+ return *string_;
+}
+
+const char* String::c_str()
+{
+ const char* c = data();
+ if (c == nullptr) {
+ return c;
+ }
+
+ // We always point into a null-terminated string so it is safe to
+ // access one past the end. If it is a null byte then we can use
+ // the pointer directly.
+ if (c[size()] == '\0') {
+ return c;
+ }
+
+ // Mutate to hold a std::string so we can get a null terminator.
+ this->internally_mutate_to_stable_string();
+ c = string_->c_str();
+ return c;
+}
+
+String& String::insert(size_type index, size_type count, char ch)
+{
+ std::string s;
+ s.reserve(size() + count);
+ s.assign(data(), size());
+ s.insert(index, count, ch);
+ return *this = std::move(s);
+}
+
+String& String::erase(size_type index, size_type count)
+{
+ if (index > size()) {
+ throw std::out_of_range("Index out of range in String::erase");
+ }
+ size_type const rcount = std::min(count, size() - index);
+ size_type const rindex = index + rcount;
+ std::string s;
+ s.reserve(size() - rcount);
+ s.assign(data(), index);
+ s.append(data() + rindex, size() - rindex);
+ return *this = std::move(s);
+}
+
+String String::substr(size_type pos, size_type count) const
+{
+ if (pos > size()) {
+ throw std::out_of_range("Index out of range in String::substr");
+ }
+ return String(*this, pos, count);
+}
+
+String::String(std::string&& s, Private)
+ : string_(std::make_shared<std::string>(std::move(s)))
+ , view_(string_->data(), string_->size())
+{
+}
+
+String::size_type String::copy(char* dest, size_type count,
+ size_type pos) const
+{
+ return view_.copy(dest, count, pos);
+}
+
+std::ostream& operator<<(std::ostream& os, String const& s)
+{
+ return os.write(s.data(), s.size());
+}
+
+std::string& operator+=(std::string& self, String const& s)
+{
+ return self += s.view();
+}
+
+String IntoString<char*>::into_string(const char* s)
+{
+ if (!s) {
+ return String();
+ }
+ return std::string(s);
+}
+
+string_view AsStringView<String>::view(String const& s)
+{
+ return s.view();
+}
+
+} // namespace cm
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
new file mode 100644
index 0000000..1623a43
--- /dev/null
+++ b/Source/cmString.hxx
@@ -0,0 +1,815 @@
+/* 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)
+ : string_()
+ , 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
diff --git a/Source/cm_static_string_view.hxx b/Source/cm_static_string_view.hxx
new file mode 100644
index 0000000..1bef0c6
--- /dev/null
+++ b/Source/cm_static_string_view.hxx
@@ -0,0 +1,41 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cm_static_string_view_hxx
+#define cm_static_string_view_hxx
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cm_string_view.hxx"
+
+#include <cstddef>
+
+namespace cm {
+
+/** A string_view that only binds to static storage.
+ *
+ * This is used together with the `""_s` user-defined literal operator
+ * to construct a type-safe abstraction of a string_view that only views
+ * statically allocated strings. These strings are const and available
+ * for the entire lifetime of the program.
+ */
+class static_string_view : public string_view
+{
+ static_string_view(string_view v)
+ : string_view(v)
+ {
+ }
+
+ friend static_string_view operator"" _s(const char* data, size_t size);
+};
+
+/** Create a static_string_view using `""_s` literal syntax. */
+inline static_string_view operator"" _s(const char* data, size_t size)
+{
+ return string_view(data, size);
+}
+
+} // namespace cm
+
+using cm::operator"" _s;
+
+#endif
diff --git a/Source/cm_string_view.cxx b/Source/cm_string_view.cxx
new file mode 100644
index 0000000..61fa80e
--- /dev/null
+++ b/Source/cm_string_view.cxx
@@ -0,0 +1,301 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cm_string_view.hxx"
+
+#ifndef CMake_HAVE_CXX_STRING_VIEW
+
+# include "cm_kwiml.h"
+
+# include <algorithm>
+# include <ostream>
+# include <stdexcept>
+
+namespace cm {
+
+string_view::const_reference string_view::at(size_type pos) const
+{
+ if (pos >= size_) {
+ throw std::out_of_range("Index out of range in string_view::at");
+ }
+ return data_[pos];
+}
+
+string_view::size_type string_view::copy(char* dest, size_type count,
+ size_type pos) const
+{
+ if (pos > size_) {
+ throw std::out_of_range("Index out of range in string_view::copy");
+ }
+ size_type const rcount = std::min(count, size_ - pos);
+ traits_type::copy(dest, data_ + pos, rcount);
+ return rcount;
+}
+
+string_view string_view::substr(size_type pos, size_type count) const
+{
+ if (pos > size_) {
+ throw std::out_of_range("Index out of range in string_view::substr");
+ }
+ size_type const rcount = std::min(count, size_ - pos);
+ return string_view(data_ + pos, rcount);
+}
+
+int string_view::compare(string_view v) const noexcept
+{
+ size_type const rlen = std::min(size_, v.size_);
+ int c = traits_type::compare(data_, v.data_, rlen);
+ if (c == 0) {
+ if (size_ < v.size_) {
+ c = -1;
+ } else if (size_ > v.size_) {
+ c = 1;
+ }
+ }
+ return c;
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v) const
+{
+ return substr(pos1, count1).compare(v);
+}
+
+int string_view::compare(size_type pos1, size_type count1, string_view v,
+ size_type pos2, size_type count2) const
+{
+ return substr(pos1, count1).compare(v.substr(pos2, count2));
+}
+
+int string_view::compare(const char* s) const
+{
+ return compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s) const
+{
+ return substr(pos1, count1).compare(string_view(s));
+}
+
+int string_view::compare(size_type pos1, size_type count1, const char* s,
+ size_type count2) const
+{
+ return substr(pos1, count1).compare(string_view(s, count2));
+}
+
+string_view::size_type string_view::find(string_view v, size_type pos) const
+ noexcept
+{
+ for (; pos + v.size_ <= size_; ++pos) {
+ if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == 0) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find(char c, size_type pos) const noexcept
+{
+ return find(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos,
+ size_type count) const
+{
+ return find(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find(const char* s, size_type pos) const
+{
+ return find(string_view(s), pos);
+}
+
+string_view::size_type string_view::rfind(string_view v, size_type pos) const
+ noexcept
+{
+ if (size_ >= v.size_) {
+ for (pos = std::min(pos, size_ - v.size_) + 1; pos > 0;) {
+ --pos;
+ if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) ==
+ 0) {
+ return pos;
+ }
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::rfind(char c, size_type pos) const noexcept
+{
+ return rfind(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos,
+ size_type count) const
+{
+ return rfind(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::rfind(const char* s, size_type pos) const
+{
+ return rfind(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_of(string_view v,
+ size_type pos) const noexcept
+{
+ for (; pos < size_; ++pos) {
+ if (traits_type::find(v.data_, v.size_, data_[pos])) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_first_of(char c, size_type pos) const
+ noexcept
+{
+ return find_first_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s, size_type pos,
+ size_type count) const
+{
+ return find_first_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_of(const char* s,
+ size_type pos) const
+{
+ return find_first_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_of(string_view v,
+ size_type pos) const noexcept
+{
+ if (size_ > 0) {
+ for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+ --pos;
+ if (traits_type::find(v.data_, v.size_, data_[pos])) {
+ return pos;
+ }
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_last_of(char c, size_type pos) const
+ noexcept
+{
+ return find_last_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s, size_type pos,
+ size_type count) const
+{
+ return find_last_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_of(const char* s,
+ size_type pos) const
+{
+ return find_last_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(string_view v,
+ size_type pos) const
+ noexcept
+{
+ for (; pos < size_; ++pos) {
+ if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_first_not_of(char c,
+ size_type pos) const
+ noexcept
+{
+ return find_first_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+ size_type pos,
+ size_type count) const
+{
+ return find_first_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_first_not_of(const char* s,
+ size_type pos) const
+{
+ return find_first_not_of(string_view(s), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(string_view v,
+ size_type pos) const
+ noexcept
+{
+ if (size_ > 0) {
+ for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) {
+ --pos;
+ if (!traits_type::find(v.data_, v.size_, data_[pos])) {
+ return pos;
+ }
+ }
+ }
+ return npos;
+}
+
+string_view::size_type string_view::find_last_not_of(char c,
+ size_type pos) const
+ noexcept
+{
+ return find_last_not_of(string_view(&c, 1), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+ size_type pos,
+ size_type count) const
+{
+ return find_last_not_of(string_view(s, count), pos);
+}
+
+string_view::size_type string_view::find_last_not_of(const char* s,
+ size_type pos) const
+{
+ return find_last_not_of(string_view(s), pos);
+}
+
+std::ostream& operator<<(std::ostream& o, string_view v)
+{
+ return o.write(v.data(), v.size());
+}
+
+std::string& operator+=(std::string& s, string_view v)
+{
+ s.append(v.data(), v.size());
+ return s;
+}
+}
+
+std::hash<cm::string_view>::result_type std::hash<cm::string_view>::operator()(
+ argument_type const& s) const noexcept
+{
+ // FNV-1a hash.
+ static KWIML_INT_uint64_t const fnv_offset_basis = 0xcbf29ce484222325;
+ static KWIML_INT_uint64_t const fnv_prime = 0x100000001b3;
+ KWIML_INT_uint64_t h = fnv_offset_basis;
+ for (char const& c : s) {
+ h = h ^ KWIML_INT_uint64_t(KWIML_INT_uint8_t(c));
+ h = h * fnv_prime;
+ }
+ return result_type(h);
+}
+#else
+// Avoid empty translation unit.
+void cm_string_view_cxx()
+{
+}
+#endif
diff --git a/Source/cm_string_view.hxx b/Source/cm_string_view.hxx
new file mode 100644
index 0000000..d368ed8
--- /dev/null
+++ b/Source/cm_string_view.hxx
@@ -0,0 +1,217 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cm_string_view_hxx
+#define cm_string_view_hxx
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
+# define CMake_HAVE_CXX_STRING_VIEW
+#endif
+
+#ifdef CMake_HAVE_CXX_STRING_VIEW
+# include <string_view>
+namespace cm {
+using std::string_view;
+}
+#else
+# include <cstddef>
+# include <functional>
+# include <iosfwd>
+# include <iterator>
+# include <string>
+
+namespace cm {
+
+class string_view
+{
+public:
+ using traits_type = std::string::traits_type;
+ using value_type = char;
+ using pointer = char*;
+ using const_pointer = const char*;
+ using reference = char&;
+ using const_reference = char const&;
+ using const_iterator = const char*;
+ using iterator = const_iterator;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using reverse_iterator = const_reverse_iterator;
+ using size_type = std::string::size_type;
+ using difference_type = std::string::difference_type;
+
+ static size_type const npos = static_cast<size_type>(-1);
+
+ string_view() noexcept = default;
+ string_view(string_view const&) noexcept = default;
+
+ string_view(const char* s, size_t count) noexcept
+ : data_(s)
+ , size_(count)
+ {
+ }
+
+ string_view(const char* s) noexcept
+ : data_(s)
+ , size_(traits_type::length(s))
+ {
+ }
+
+ // C++17 does not define this constructor. Instead it defines
+ // a conversion operator on std::string to create a string_view.
+ // Since this implementation is used in C++11, std::string does
+ // not have that conversion.
+ string_view(std::string const& s) noexcept
+ : data_(s.data())
+ , size_(s.size())
+ {
+ }
+
+ // C++17 does not define this conversion. Instead it defines
+ // a constructor on std::string that can take a string_view.
+ // Since this implementation is used in C++11, std::string does
+ // not have that constructor.
+ explicit operator std::string() const { return std::string(data_, size_); }
+
+ string_view& operator=(string_view const&) = default;
+
+ const_iterator begin() const noexcept { return data_; }
+ const_iterator end() const noexcept { return data_ + size_; }
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator cend() const noexcept { return end(); }
+
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator rend() const noexcept
+ {
+ return const_reverse_iterator(begin());
+ }
+ const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ const_reverse_iterator crend() const noexcept { return rend(); }
+
+ const_reference operator[](size_type pos) const noexcept
+ {
+ return data_[pos];
+ }
+ const_reference at(size_type pos) const;
+ const_reference front() const noexcept { return data_[0]; }
+ const_reference back() const noexcept { return data_[size_ - 1]; }
+ const_pointer data() const noexcept { return data_; }
+
+ size_type size() const noexcept { return size_; }
+ size_type length() const noexcept { return size_; }
+ size_type max_size() const noexcept { return npos - 1; }
+ bool empty() const noexcept { return size_ == 0; }
+
+ void remove_prefix(size_type n) noexcept
+ {
+ data_ += n;
+ size_ -= n;
+ }
+ void remove_suffix(size_type n) noexcept { size_ -= n; }
+ void swap(string_view& v) noexcept
+ {
+ string_view tmp = v;
+ v = *this;
+ *this = tmp;
+ }
+
+ size_type copy(char* dest, size_type count, size_type pos = 0) const;
+ string_view substr(size_type pos = 0, size_type count = npos) const;
+
+ int compare(string_view v) const noexcept;
+ int compare(size_type pos1, size_type count1, string_view v) const;
+ int compare(size_type pos1, size_type count1, string_view v, size_type pos2,
+ size_type count2) const;
+ int compare(const char* s) const;
+ int compare(size_type pos1, size_type count1, const char* s) const;
+ int compare(size_type pos1, size_type count1, const char* s,
+ size_type count2) const;
+
+ size_type find(string_view v, size_type pos = 0) const noexcept;
+ size_type find(char c, size_type pos = 0) const noexcept;
+ size_type find(const char* s, size_type pos, size_type count) const;
+ size_type find(const char* s, size_type pos = 0) const;
+
+ size_type rfind(string_view v, size_type pos = npos) const noexcept;
+ size_type rfind(char c, size_type pos = npos) const noexcept;
+ size_type rfind(const char* s, size_type pos, size_type count) const;
+ size_type rfind(const char* s, size_type pos = npos) const;
+
+ size_type find_first_of(string_view v, size_type pos = 0) const noexcept;
+ size_type find_first_of(char c, size_type pos = 0) const noexcept;
+ size_type find_first_of(const char* s, size_type pos, size_type count) const;
+ size_type find_first_of(const char* s, size_type pos = 0) const;
+
+ size_type find_last_of(string_view v, size_type pos = npos) const noexcept;
+ size_type find_last_of(char c, size_type pos = npos) const noexcept;
+ size_type find_last_of(const char* s, size_type pos, size_type count) const;
+ size_type find_last_of(const char* s, size_type pos = npos) const;
+
+ size_type find_first_not_of(string_view v, size_type pos = 0) const noexcept;
+ size_type find_first_not_of(char c, size_type pos = 0) const noexcept;
+ size_type find_first_not_of(const char* s, size_type pos,
+ size_type count) const;
+ size_type find_first_not_of(const char* s, size_type pos = 0) const;
+
+ size_type find_last_not_of(string_view v, size_type pos = npos) const
+ noexcept;
+ size_type find_last_not_of(char c, size_type pos = npos) const noexcept;
+ size_type find_last_not_of(const char* s, size_type pos,
+ size_type count) const;
+ size_type find_last_not_of(const char* s, size_type pos = npos) const;
+
+private:
+ const char* data_ = nullptr;
+ size_type size_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& o, string_view v);
+
+std::string& operator+=(std::string& s, string_view v);
+
+inline bool operator==(string_view l, string_view r) noexcept
+{
+ return l.compare(r) == 0;
+}
+
+inline bool operator!=(string_view l, string_view r) noexcept
+{
+ return l.compare(r) != 0;
+}
+
+inline bool operator<(string_view l, string_view r) noexcept
+{
+ return l.compare(r) < 0;
+}
+
+inline bool operator<=(string_view l, string_view r) noexcept
+{
+ return l.compare(r) <= 0;
+}
+
+inline bool operator>(string_view l, string_view r) noexcept
+{
+ return l.compare(r) > 0;
+}
+
+inline bool operator>=(string_view l, string_view r) noexcept
+{
+ return l.compare(r) >= 0;
+}
+}
+
+namespace std {
+
+template <>
+struct hash<cm::string_view>
+{
+ typedef cm::string_view argument_type;
+ typedef size_t result_type;
+ result_type operator()(argument_type const& s) const noexcept;
+};
+}
+
+#endif
+#endif