[libc-commits] [libc] e0aece2 - [libc][utils] Add more methods to StringView
Guillaume Chatelet via libc-commits
libc-commits at lists.llvm.org
Tue Jul 12 00:42:38 PDT 2022
Author: Guillaume Chatelet
Date: 2022-07-12T07:42:29Z
New Revision: e0aece276faa99fdeb0f7a5166312bb05a0fe1dc
URL: https://github.com/llvm/llvm-project/commit/e0aece276faa99fdeb0f7a5166312bb05a0fe1dc
DIFF: https://github.com/llvm/llvm-project/commit/e0aece276faa99fdeb0f7a5166312bb05a0fe1dc.diff
LOG: [libc][utils] Add more methods to StringView
Differential Revision: https://reviews.llvm.org/D128908
Added:
Modified:
libc/src/__support/CPP/StringView.h
libc/test/src/__support/CPP/stringview_test.cpp
Removed:
################################################################################
diff --git a/libc/src/__support/CPP/StringView.h b/libc/src/__support/CPP/StringView.h
index 8c0cfd61e0977..4caec808f06e7 100644
--- a/libc/src/__support/CPP/StringView.h
+++ b/libc/src/__support/CPP/StringView.h
@@ -24,7 +24,20 @@ class StringView {
const char *Data;
size_t Len;
+ static size_t min(size_t A, size_t B) { return A <= B ? A : B; }
+
+ static int compareMemory(const char *Lhs, const char *Rhs, size_t Length) {
+ for (size_t I = 0; I < Length; ++I)
+ if (int Diff = (int)Lhs[I] - (int)Rhs[I])
+ return Diff;
+ return 0;
+ }
+
public:
+ // special value equal to the maximum value representable by the type
+ // size_type.
+ static constexpr size_t npos = -1;
+
StringView() : Data(nullptr), Len(0) {}
// Assumes Str is a null-terminated string. The length of the string does
@@ -41,18 +54,67 @@ class StringView {
explicit StringView(const char *Str, size_t N)
: Data(N ? Str : nullptr), Len(Str == nullptr ? 0 : N) {}
+ // Ctor for raw literal.
+ template <size_t N>
+ StringView(const char (&Str)[N]) : StringView(Str, N - 1) {}
+
const char *data() const { return Data; }
- size_t size() { return Len; }
+ // Returns the size of the StringView.
+ size_t size() const { return Len; }
+
+ // Returns whether the StringView is empty.
+ bool empty() const { return Len == 0; }
+
+ // Returns an iterator to the first character of the view.
+ const char *begin() const { return Data; }
+ // Returns an iterator to the character following the last character of the
+ // view.
+ const char *end() const { return Data + Len; }
+
+ // Returns a const reference to the character at specified location pos.
+ // No bounds checking is performed: the behavior is undefined if pos >=
+ // size().
const char &operator[](size_t Index) const { return Data[Index]; }
+ /// compare - Compare two strings; the result is -1, 0, or 1 if this string
+ /// is lexicographically less than, equal to, or greater than the \p Other.
+ int compare(StringView Other) const {
+ // Check the prefix for a mismatch.
+ if (int Res = compareMemory(Data, Other.Data, min(Len, Other.Len)))
+ return Res < 0 ? -1 : 1;
+ // Otherwise the prefixes match, so we only need to check the lengths.
+ if (Len == Other.Len)
+ return 0;
+ return Len < Other.Len ? -1 : 1;
+ }
+
+ // An equivalent method is not available in std::string_view.
+ bool equals(StringView Other) const {
+ return (Len == Other.Len &&
+ compareMemory(Data, Other.Data, Other.Len) == 0);
+ }
+
+ inline bool operator==(StringView Other) const { return equals(Other); }
+ inline bool operator!=(StringView Other) const { return !(*this == Other); }
+ inline bool operator<(StringView Other) const { return compare(Other) == -1; }
+ inline bool operator<=(StringView Other) const { return compare(Other) != 1; }
+ inline bool operator>(StringView Other) const { return compare(Other) == 1; }
+ inline bool operator>=(StringView Other) const {
+ return compare(Other) != -1;
+ }
+
+ // Moves the start of the view forward by n characters.
+ // The behavior is undefined if n > size().
StringView remove_prefix(size_t N) const {
if (N >= Len)
return StringView();
return StringView(Data + N, Len - N);
}
+ // Moves the end of the view back by n characters.
+ // The behavior is undefined if n > size().
StringView remove_suffix(size_t N) const {
if (N >= Len)
return StringView();
@@ -61,45 +123,134 @@ class StringView {
// An equivalent method is not available in std::string_view.
StringView trim(char C) const {
- if (Len == 0)
- return StringView();
+ StringView Copy = *this;
+ while (!Copy.empty() && Copy.front() == C)
+ Copy = Copy.drop_front();
+ while (!Copy.empty() && Copy.back() == C)
+ Copy = Copy.drop_back();
+ return Copy;
+ }
- const char *NewStart = Data;
- size_t PrefixLen = 0;
- for (; PrefixLen < Len; ++NewStart, ++PrefixLen) {
- if (*NewStart != C)
- break;
- }
+ // Check if this string starts with the given Prefix.
+ bool starts_with(StringView Prefix) const {
+ return Len >= Prefix.Len &&
+ compareMemory(Data, Prefix.Data, Prefix.Len) == 0;
+ }
+
+ // Check if this string ends with the given Suffix.
+ bool ends_with(StringView Suffix) const {
+ return Len >= Suffix.Len &&
+ compareMemory(end() - Suffix.Len, Suffix.Data, Suffix.Len) == 0;
+ }
+
+ // Return a reference to the substring from [Start, Start + N).
+ //
+ // Start The index of the starting character in the substring; if the index is
+ // npos or greater than the length of the string then the empty substring will
+ // be returned.
+ //
+ // N The number of characters to included in the substring. If N exceeds the
+ // number of characters remaining in the string, the string suffix (starting
+ // with Start) will be returned.
+ StringView substr(size_t Start, size_t N = npos) const {
+ Start = min(Start, Len);
+ return StringView(Data + Start, min(N, Len - Start));
+ }
- size_t SuffixLen = 0;
- const char *NewEnd = Data + Len - 1;
- for (; SuffixLen < Len; --NewEnd, ++SuffixLen) {
- if (*NewEnd != C)
- break;
+ // Search for the first character satisfying the predicate Function
+ //
+ // Returns The index of the first character satisfying Function starting from
+ // From, or npos if not found.
+ template <typename F> size_t find_if(F Function, size_t From = 0) const {
+ StringView S = drop_front(From);
+ while (!S.empty()) {
+ if (Function(S.front()))
+ return size() - S.size();
+ S = S.drop_front();
}
+ return npos;
+ }
- return remove_prefix(PrefixLen).remove_suffix(SuffixLen);
+ // Search for the first character not satisfying the predicate Function
+ // Returns The index of the first character not satisfying Function starting
+ // from From, or npos if not found.
+ template <typename F> size_t find_if_not(F Function, size_t From = 0) const {
+ return find_if([Function](char c) { return !Function(c); }, From);
}
- // Check if this string starts with the given \p Prefix.
- bool starts_with(StringView Prefix) const {
- if (Len < Prefix.Len)
+ // front - Get the first character in the string.
+ char front() const { return Data[0]; }
+
+ // back - Get the last character in the string.
+ char back() const { return Data[Len - 1]; }
+
+ // Return a StringView equal to 'this' but with the first N elements
+ // dropped.
+ StringView drop_front(size_t N = 1) const { return substr(N); }
+
+ // Return a StringView equal to 'this' but with the last N elements
+ // dropped.
+ StringView drop_back(size_t N = 1) const { return substr(0, size() - N); }
+
+ // Return a StringView equal to 'this' but with only the first N
+ // elements remaining. If N is greater than the length of the
+ // string, the entire string is returned.
+ StringView take_front(size_t N = 1) const {
+ if (N >= size())
+ return *this;
+ return drop_back(size() - N);
+ }
+
+ // Return a StringView equal to 'this' but with only the last N
+ // elements remaining. If N is greater than the length of the
+ // string, the entire string is returned.
+ StringView take_back(size_t N = 1) const {
+ if (N >= size())
+ return *this;
+ return drop_front(size() - N);
+ }
+
+ // Return the longest prefix of 'this' such that every character
+ // in the prefix satisfies the given predicate.
+ template <typename F> StringView take_while(F Function) const {
+ return substr(0, find_if_not(Function));
+ }
+
+ // Return the longest prefix of 'this' such that no character in
+ // the prefix satisfies the given predicate.
+ template <typename F> StringView take_until(F Function) const {
+ return substr(0, find_if(Function));
+ }
+
+ // Return a StringView equal to 'this', but with all characters satisfying
+ // the given predicate dropped from the beginning of the string.
+ template <typename F> StringView drop_while(F Function) const {
+ return substr(find_if_not(Function));
+ }
+
+ // Return a StringView equal to 'this', but with all characters not
+ // satisfying the given predicate dropped from the beginning of the string.
+ template <typename F> StringView drop_until(F Function) const {
+ return substr(find_if(Function));
+ }
+
+ // Returns true if this StringView has the given prefix and removes that
+ // prefix.
+ bool consume_front(StringView Prefix) {
+ if (!starts_with(Prefix))
return false;
- for (size_t I = 0; I < Prefix.Len; ++I) {
- if (Data[I] != Prefix.Data[I])
- return false;
- }
+
+ *this = drop_front(Prefix.size());
return true;
}
- // An equivalent method is not available in std::string_view.
- bool equals(StringView Other) const {
- if (Len != Other.Len)
+ // Returns true if this StringView has the given suffix and removes that
+ // suffix.
+ bool consume_back(StringView Suffix) {
+ if (!ends_with(Suffix))
return false;
- for (size_t I = 0; I < Len; ++I) {
- if (Data[I] != Other.Data[I])
- return false;
- }
+
+ *this = drop_back(Suffix.size());
return true;
}
};
diff --git a/libc/test/src/__support/CPP/stringview_test.cpp b/libc/test/src/__support/CPP/stringview_test.cpp
index 7835eb667d8df..9f90eadd6b925 100644
--- a/libc/test/src/__support/CPP/stringview_test.cpp
+++ b/libc/test/src/__support/CPP/stringview_test.cpp
@@ -9,65 +9,67 @@
#include "src/__support/CPP/StringView.h"
#include "utils/UnitTest/Test.h"
+using __llvm_libc::cpp::StringView;
+
TEST(LlvmLibcStringViewTest, InitializeCheck) {
- __llvm_libc::cpp::StringView v;
+ StringView v;
ASSERT_EQ(v.size(), size_t(0));
ASSERT_TRUE(v.data() == nullptr);
- v = __llvm_libc::cpp::StringView("");
+ v = StringView("");
ASSERT_EQ(v.size(), size_t(0));
ASSERT_TRUE(v.data() == nullptr);
- v = __llvm_libc::cpp::StringView(nullptr);
+ v = StringView(nullptr);
ASSERT_EQ(v.size(), size_t(0));
ASSERT_TRUE(v.data() == nullptr);
- v = __llvm_libc::cpp::StringView(nullptr, 10);
+ v = StringView(nullptr, 10);
ASSERT_EQ(v.size(), size_t(0));
ASSERT_TRUE(v.data() == nullptr);
- v = __llvm_libc::cpp::StringView("abc", 0);
+ v = StringView("abc", 0);
ASSERT_EQ(v.size(), size_t(0));
ASSERT_TRUE(v.data() == nullptr);
- v = __llvm_libc::cpp::StringView("123456789");
+ v = StringView("123456789");
ASSERT_EQ(v.size(), size_t(9));
}
TEST(LlvmLibcStringViewTest, Equals) {
- __llvm_libc::cpp::StringView v("abc");
- ASSERT_TRUE(v.equals(__llvm_libc::cpp::StringView("abc")));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView()));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("")));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("123")));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("abd")));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("aaa")));
- ASSERT_FALSE(v.equals(__llvm_libc::cpp::StringView("abcde")));
+ StringView v("abc");
+ ASSERT_TRUE(v.equals(StringView("abc")));
+ ASSERT_FALSE(v.equals(StringView()));
+ ASSERT_FALSE(v.equals(StringView("")));
+ ASSERT_FALSE(v.equals(StringView("123")));
+ ASSERT_FALSE(v.equals(StringView("abd")));
+ ASSERT_FALSE(v.equals(StringView("aaa")));
+ ASSERT_FALSE(v.equals(StringView("abcde")));
}
TEST(LlvmLibcStringViewTest, startsWith) {
- __llvm_libc::cpp::StringView v("abc");
- ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("a")));
- ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("ab")));
- ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("abc")));
- ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView()));
- ASSERT_TRUE(v.starts_with(__llvm_libc::cpp::StringView("")));
- ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("123")));
- ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abd")));
- ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("aaa")));
- ASSERT_FALSE(v.starts_with(__llvm_libc::cpp::StringView("abcde")));
+ StringView v("abc");
+ ASSERT_TRUE(v.starts_with(StringView("a")));
+ ASSERT_TRUE(v.starts_with(StringView("ab")));
+ ASSERT_TRUE(v.starts_with(StringView("abc")));
+ ASSERT_TRUE(v.starts_with(StringView()));
+ ASSERT_TRUE(v.starts_with(StringView("")));
+ ASSERT_FALSE(v.starts_with(StringView("123")));
+ ASSERT_FALSE(v.starts_with(StringView("abd")));
+ ASSERT_FALSE(v.starts_with(StringView("aaa")));
+ ASSERT_FALSE(v.starts_with(StringView("abcde")));
}
TEST(LlvmLibcStringViewTest, RemovePrefix) {
- __llvm_libc::cpp::StringView v("123456789");
+ StringView v("123456789");
auto p = v.remove_prefix(0);
ASSERT_EQ(p.size(), size_t(9));
- ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("123456789")));
+ ASSERT_TRUE(p.equals(StringView("123456789")));
p = v.remove_prefix(4);
ASSERT_EQ(p.size(), size_t(5));
- ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("56789")));
+ ASSERT_TRUE(p.equals(StringView("56789")));
p = v.remove_prefix(9);
ASSERT_EQ(p.size(), size_t(0));
@@ -79,15 +81,15 @@ TEST(LlvmLibcStringViewTest, RemovePrefix) {
}
TEST(LlvmLibcStringViewTest, RemoveSuffix) {
- __llvm_libc::cpp::StringView v("123456789");
+ StringView v("123456789");
auto p = v.remove_suffix(0);
ASSERT_EQ(p.size(), size_t(9));
- ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("123456789")));
+ ASSERT_TRUE(p.equals(StringView("123456789")));
p = v.remove_suffix(4);
ASSERT_EQ(p.size(), size_t(5));
- ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("12345")));
+ ASSERT_TRUE(p.equals(StringView("12345")));
p = v.remove_suffix(9);
ASSERT_EQ(p.size(), size_t(0));
@@ -99,42 +101,78 @@ TEST(LlvmLibcStringViewTest, RemoveSuffix) {
}
TEST(LlvmLibcStringViewTest, TrimSingleChar) {
- __llvm_libc::cpp::StringView v(" 123456789 ");
+ StringView v(" 123456789 ");
auto t = v.trim(' ');
ASSERT_EQ(t.size(), size_t(9));
- ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("123456789")));
+ ASSERT_TRUE(t.equals(StringView("123456789")));
- v = __llvm_libc::cpp::StringView("====12345==");
+ v = StringView("====12345==");
t = v.trim(' ');
ASSERT_EQ(v.size(), size_t(11));
- ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("====12345==")));
+ ASSERT_TRUE(t.equals(StringView("====12345==")));
t = v.trim('=');
ASSERT_EQ(t.size(), size_t(5));
- ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+ ASSERT_TRUE(t.equals(StringView("12345")));
- v = __llvm_libc::cpp::StringView("12345===");
+ v = StringView("12345===");
t = v.trim('=');
ASSERT_EQ(t.size(), size_t(5));
- ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+ ASSERT_TRUE(t.equals(StringView("12345")));
- v = __llvm_libc::cpp::StringView("===========12345");
+ v = StringView("===========12345");
t = v.trim('=');
ASSERT_EQ(t.size(), size_t(5));
- ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+ ASSERT_TRUE(t.equals(StringView("12345")));
- v = __llvm_libc::cpp::StringView("============");
+ v = StringView("============");
t = v.trim('=');
ASSERT_EQ(t.size(), size_t(0));
ASSERT_TRUE(t.data() == nullptr);
- v = __llvm_libc::cpp::StringView();
+ v = StringView();
t = v.trim(' ');
ASSERT_EQ(t.size(), size_t(0));
ASSERT_TRUE(t.data() == nullptr);
- v = __llvm_libc::cpp::StringView("");
+ v = StringView("");
t = v.trim(' ');
ASSERT_EQ(t.size(), size_t(0));
ASSERT_TRUE(t.data() == nullptr);
}
+
+TEST(LlvmLibcStringViewTest, Observer) {
+ StringView ABC("abc");
+ ASSERT_EQ(ABC.size(), size_t(3));
+ ASSERT_FALSE(ABC.empty());
+ ASSERT_EQ(ABC.front(), 'a');
+ ASSERT_EQ(ABC.back(), 'c');
+}
+
+bool isDigit(char c) { return c >= '0' && c <= '9'; }
+
+TEST(LlvmLibcStringViewTest, Transform) {
+ ASSERT_TRUE(StringView("123abc").drop_back(3).equals("123"));
+ ASSERT_TRUE(StringView("123abc").drop_front(3).equals("abc"));
+ ASSERT_TRUE(StringView("123abc").take_back(3).equals("abc"));
+ ASSERT_TRUE(StringView("123abc").take_front(3).equals("123"));
+
+ ASSERT_TRUE(StringView("123abc").take_while(&isDigit).equals("123"));
+ ASSERT_TRUE(StringView("abc123").take_until(&isDigit).equals("abc"));
+ ASSERT_TRUE(StringView("123abc").drop_while(&isDigit).equals("abc"));
+ ASSERT_TRUE(StringView("abc123").drop_until(&isDigit).equals("123"));
+}
+
+TEST(LlvmLibcStringViewTest, ConsumeFront) {
+ StringView Tmp("abc");
+ ASSERT_FALSE(Tmp.consume_front("###"));
+ ASSERT_TRUE(Tmp.consume_front("ab"));
+ ASSERT_TRUE(Tmp.equals("c"));
+}
+
+TEST(LlvmLibcStringViewTest, ConsumeBack) {
+ StringView Tmp("abc");
+ ASSERT_FALSE(Tmp.consume_back("###"));
+ ASSERT_TRUE(Tmp.consume_back("bc"));
+ ASSERT_TRUE(Tmp.equals("a"));
+}
More information about the libc-commits
mailing list