Add find() and rfind() methods to base::StringView

Will be used by upcoming CLs in the trace processor.

Test: perfetto_unittests --gtest_filter=StringViewTest*
Change-Id: I98925deb816214cf23078a02b9582d544cc7ba86
diff --git a/Android.bp b/Android.bp
index 87e7112..8317acd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -44,6 +44,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
@@ -158,6 +159,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
@@ -222,6 +224,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
@@ -399,6 +402,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
@@ -565,6 +569,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/test/test_task_runner.cc",
     "src/base/test/utils.cc",
@@ -2565,6 +2570,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
@@ -2802,6 +2808,7 @@
     "src/base/string_splitter_unittest.cc",
     "src/base/string_utils.cc",
     "src/base/string_utils_unittest.cc",
+    "src/base/string_view.cc",
     "src/base/string_view_unittest.cc",
     "src/base/string_writer_unittest.cc",
     "src/base/task_runner_unittest.cc",
@@ -3080,6 +3087,7 @@
     "src/base/pipe.cc",
     "src/base/string_splitter.cc",
     "src/base/string_utils.cc",
+    "src/base/string_view.cc",
     "src/base/temp_file.cc",
     "src/base/thread_checker.cc",
     "src/base/time.cc",
diff --git a/include/perfetto/base/string_view.h b/include/perfetto/base/string_view.h
index 9750755..648d8a5 100644
--- a/include/perfetto/base/string_view.h
+++ b/include/perfetto/base/string_view.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "perfetto/base/hash.h"
+#include "perfetto/base/logging.h"
 
 namespace perfetto {
 namespace base {
@@ -30,6 +31,8 @@
 // Strings are internally NOT null terminated.
 class StringView {
  public:
+  static constexpr size_t npos = static_cast<size_t>(-1);
+
   StringView() : data_(""), size_(0) {}
   StringView(const StringView&) = default;
   StringView& operator=(const StringView&) = default;
@@ -48,6 +51,34 @@
   size_t size() const { return size_; }
   const char* data() const { return data_; }
 
+  char at(size_t pos) const {
+    PERFETTO_DCHECK(pos < size_);
+    return data_[pos];
+  }
+
+  size_t find(char c) const {
+    for (size_t i = 0; i < size_; ++i) {
+      if (data_[i] == c)
+        return i;
+    }
+    return npos;
+  }
+
+  size_t rfind(char c) const {
+    for (size_t i = size_; i > 0; --i) {
+      if (data_[i - 1] == c)
+        return i - 1;
+    }
+    return npos;
+  }
+
+  StringView substr(size_t pos, size_t count = npos) const {
+    if (pos >= size_)
+      return StringView();
+    size_t rcount = std::min(count, size_ - pos);
+    return StringView(data_ + pos, rcount);
+  }
+
   std::string ToStdString() const { return std::string(data_, size_); }
 
   uint64_t Hash() const {
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 4546cca..25f3691 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -29,6 +29,7 @@
     "paged_memory.cc",
     "string_splitter.cc",
     "string_utils.cc",
+    "string_view.cc",
     "thread_checker.cc",
     "time.cc",
     "virtual_destructors.cc",
diff --git a/src/base/string_view.cc b/src/base/string_view.cc
new file mode 100644
index 0000000..b10239d
--- /dev/null
+++ b/src/base/string_view.cc
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/base/string_view.h"
+
+namespace perfetto {
+namespace base {
+
+// static
+constexpr size_t StringView::npos;
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/string_view_unittest.cc b/src/base/string_view_unittest.cc
index fe7f545..5b1e492 100644
--- a/src/base/string_view_unittest.cc
+++ b/src/base/string_view_unittest.cc
@@ -52,6 +52,32 @@
     EXPECT_TRUE(x == StringView("abc"));
     EXPECT_TRUE(x != StringView("abcd"));
   }
+
+  // Test find().
+  EXPECT_EQ(StringView("").find('x'), StringView::npos);
+  EXPECT_EQ(StringView("foo").find('x'), StringView::npos);
+  EXPECT_EQ(StringView("foo").find('f'), 0u);
+  EXPECT_EQ(StringView("foo").find('o'), 1u);
+
+  // Test rfind().
+  EXPECT_EQ(StringView("").rfind('x'), StringView::npos);
+  EXPECT_EQ(StringView("foo").rfind('x'), StringView::npos);
+  EXPECT_EQ(StringView("foo").rfind('f'), 0u);
+  EXPECT_EQ(StringView("foo").rfind('o'), 2u);
+
+  // Test substr().
+  EXPECT_EQ(StringView("foo").substr(3, 1).ToStdString(), "");
+  EXPECT_EQ(StringView("foo").substr(4, 0).ToStdString(), "");
+  EXPECT_EQ(StringView("foo").substr(4, 1).ToStdString(), "");
+  EXPECT_EQ(StringView("foo").substr(0, 1).ToStdString(), "f");
+  EXPECT_EQ(StringView("foo").substr(0, 3).ToStdString(), "foo");
+  EXPECT_EQ(StringView("foo").substr(0, 99).ToStdString(), "foo");
+  EXPECT_EQ(StringView("foo").substr(1, 2).ToStdString(), "oo");
+  EXPECT_EQ(StringView("foo").substr(1, 3).ToStdString(), "oo");
+  EXPECT_EQ(StringView("foo").substr(1, 99).ToStdString(), "oo");
+  EXPECT_EQ(StringView("xyz").substr(0).ToStdString(), "xyz");
+  EXPECT_EQ(StringView("xyz").substr(2).ToStdString(), "z");
+  EXPECT_EQ(StringView("xyz").substr(3).ToStdString(), "");
 }
 
 TEST(StringViewTest, HashCollisions) {