[libc] Add a standalone flavor of an equivalent of std::string_view.

This class is to serve as a replacement for llvm::StringRef as part of
the plans to limit dependency on other parts of LLVM. One use of
llvm::StringRef in MPFRWrapper has been replaced with the new class.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D97330

GitOrigin-RevId: dbb131d53aacabdf5d85299e447a970a0280b127
diff --git a/test/utils/CPP/CMakeLists.txt b/test/utils/CPP/CMakeLists.txt
index f770334..13a22c7 100644
--- a/test/utils/CPP/CMakeLists.txt
+++ b/test/utils/CPP/CMakeLists.txt
@@ -9,3 +9,13 @@
   DEPENDS
     libc.utils.CPP.standalone_cpp
 )
+
+add_libc_unittest(
+  stringview_test
+  SUITE
+    libc_cpp_utils_unittests
+  SRCS
+    stringview_test.cpp
+  DEPENDS
+    libc.utils.CPP.standalone_cpp
+)
diff --git a/test/utils/CPP/stringview_test.cpp b/test/utils/CPP/stringview_test.cpp
new file mode 100644
index 0000000..b88bcc3
--- /dev/null
+++ b/test/utils/CPP/stringview_test.cpp
@@ -0,0 +1,127 @@
+//===-- Unittests for StringView ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "utils/CPP/StringView.h"
+#include "utils/UnitTest/Test.h"
+
+TEST(LlvmLibcStringViewTest, InitializeCheck) {
+  __llvm_libc::cpp::StringView v;
+  ASSERT_EQ(v.size(), size_t(0));
+  ASSERT_TRUE(v.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView("");
+  ASSERT_EQ(v.size(), size_t(0));
+  ASSERT_TRUE(v.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView(nullptr);
+  ASSERT_EQ(v.size(), size_t(0));
+  ASSERT_TRUE(v.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView(nullptr, 10);
+  ASSERT_EQ(v.size(), size_t(0));
+  ASSERT_TRUE(v.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView("abc", 0);
+  ASSERT_EQ(v.size(), size_t(0));
+  ASSERT_TRUE(v.data() == nullptr);
+
+  v = __llvm_libc::cpp::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")));
+}
+
+TEST(LlvmLibcStringViewTest, RemovePrefix) {
+  __llvm_libc::cpp::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")));
+
+  p = v.remove_prefix(4);
+  ASSERT_EQ(p.size(), size_t(5));
+  ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("56789")));
+
+  p = v.remove_prefix(9);
+  ASSERT_EQ(p.size(), size_t(0));
+  ASSERT_TRUE(p.data() == nullptr);
+
+  p = v.remove_prefix(10);
+  ASSERT_EQ(p.size(), size_t(0));
+  ASSERT_TRUE(p.data() == nullptr);
+}
+
+TEST(LlvmLibcStringViewTest, RemoveSuffix) {
+  __llvm_libc::cpp::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")));
+
+  p = v.remove_suffix(4);
+  ASSERT_EQ(p.size(), size_t(5));
+  ASSERT_TRUE(p.equals(__llvm_libc::cpp::StringView("12345")));
+
+  p = v.remove_suffix(9);
+  ASSERT_EQ(p.size(), size_t(0));
+  ASSERT_TRUE(p.data() == nullptr);
+
+  p = v.remove_suffix(10);
+  ASSERT_EQ(p.size(), size_t(0));
+  ASSERT_TRUE(p.data() == nullptr);
+}
+
+TEST(LlvmLibcStringViewTest, TrimSingleChar) {
+  __llvm_libc::cpp::StringView v("     123456789   ");
+  auto t = v.trim(' ');
+  ASSERT_EQ(t.size(), size_t(9));
+  ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("123456789")));
+
+  v = __llvm_libc::cpp::StringView("====12345==");
+  t = v.trim(' ');
+  ASSERT_EQ(v.size(), size_t(11));
+  ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("====12345==")));
+
+  t = v.trim('=');
+  ASSERT_EQ(t.size(), size_t(5));
+  ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+
+  v = __llvm_libc::cpp::StringView("12345===");
+  t = v.trim('=');
+  ASSERT_EQ(t.size(), size_t(5));
+  ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+
+  v = __llvm_libc::cpp::StringView("===========12345");
+  t = v.trim('=');
+  ASSERT_EQ(t.size(), size_t(5));
+  ASSERT_TRUE(t.equals(__llvm_libc::cpp::StringView("12345")));
+
+  v = __llvm_libc::cpp::StringView("============");
+  t = v.trim('=');
+  ASSERT_EQ(t.size(), size_t(0));
+  ASSERT_TRUE(t.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView();
+  t = v.trim(' ');
+  ASSERT_EQ(t.size(), size_t(0));
+  ASSERT_TRUE(t.data() == nullptr);
+
+  v = __llvm_libc::cpp::StringView("");
+  t = v.trim(' ');
+  ASSERT_EQ(t.size(), size_t(0));
+  ASSERT_TRUE(t.data() == nullptr);
+}
diff --git a/utils/CPP/CMakeLists.txt b/utils/CPP/CMakeLists.txt
index e321150..67483ab 100644
--- a/utils/CPP/CMakeLists.txt
+++ b/utils/CPP/CMakeLists.txt
@@ -5,5 +5,6 @@
     ArrayRef.h
     Bitset.h
     Functional.h
+    StringView.h
     TypeTraits.h
 )
diff --git a/utils/CPP/StringView.h b/utils/CPP/StringView.h
new file mode 100644
index 0000000..2e7b3a0
--- /dev/null
+++ b/utils/CPP/StringView.h
@@ -0,0 +1,97 @@
+//===-- Standalone implementation std::string_view --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_UTILS_CPP_STRINGVIEW_H
+#define LLVM_LIBC_UTILS_CPP_STRINGVIEW_H
+
+#include <stddef.h>
+
+namespace __llvm_libc {
+namespace cpp {
+
+// This is very simple alternate of the std::string_view class. There is no
+// bounds check performed in any of the methods. The callers are expected to
+// do the checks before invoking the methods.
+//
+// This class will be extended as needed in future.
+class StringView {
+private:
+  const char *Data;
+  size_t Len;
+
+public:
+  StringView() : Data(nullptr), Len(0) {}
+
+  // Assumes Str is a null-terminated string. The length of the string does
+  // not include the terminating null character.
+  explicit StringView(const char *Str) : Data(Str), Len(0) {
+    if (Str == nullptr)
+      return;
+    for (const char *D = Data; *D != '\0'; ++D, ++Len)
+      ;
+    if (Len == 0)
+      Data = nullptr;
+  }
+
+  explicit StringView(const char *Str, size_t N)
+      : Data(N ? Str : nullptr), Len(Str == nullptr ? 0 : N) {}
+
+  const char *data() const { return Data; }
+
+  size_t size() { return Len; }
+
+  StringView remove_prefix(size_t N) const {
+    if (N >= Len)
+      return StringView();
+    return StringView(Data + N, Len - N);
+  }
+
+  StringView remove_suffix(size_t N) const {
+    if (N >= Len)
+      return StringView();
+    return StringView(Data, Len - N);
+  }
+
+  // An equivalent method is not available in std::string_view.
+  StringView trim(char C) const {
+    if (Len == 0)
+      return StringView();
+
+    const char *NewStart = Data;
+    size_t PrefixLen = 0;
+    for (; PrefixLen < Len; ++NewStart, ++PrefixLen) {
+      if (*NewStart != C)
+        break;
+    }
+
+    size_t SuffixLen = 0;
+    const char *NewEnd = Data + Len - 1;
+    for (; SuffixLen < Len; --NewEnd, ++SuffixLen) {
+      if (*NewEnd != C)
+        break;
+    }
+
+    return remove_prefix(PrefixLen).remove_suffix(SuffixLen);
+  }
+
+  // An equivalent method is not available in std::string_view.
+  bool equals(StringView Other) const {
+    if (Len != Other.Len)
+      return false;
+    for (size_t I = 0; I < Len; ++I) {
+      if (Data[I] != Other.Data[I])
+        return false;
+    }
+    return true;
+  }
+};
+
+} // namespace cpp
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_UTILS_CPP_STRINGVIEW_H
diff --git a/utils/MPFRWrapper/CMakeLists.txt b/utils/MPFRWrapper/CMakeLists.txt
index cc66d1c..6f1bb76 100644
--- a/utils/MPFRWrapper/CMakeLists.txt
+++ b/utils/MPFRWrapper/CMakeLists.txt
@@ -12,8 +12,8 @@
     MPFRUtils.cpp
     MPFRUtils.h
   )
-  add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp libc.utils.FPUtil.fputil LibcUnitTest LLVMSupport)
-  target_link_libraries(libcMPFRWrapper -lmpfr -lgmp LibcFPTestHelpers LibcUnitTest LLVMSupport)
+  add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp libc.utils.FPUtil.fputil LibcUnitTest)
+  target_link_libraries(libcMPFRWrapper -lmpfr -lgmp LibcFPTestHelpers LibcUnitTest)
 else()
   message(WARNING "Math tests using MPFR will be skipped.")
 endif()
diff --git a/utils/MPFRWrapper/MPFRUtils.cpp b/utils/MPFRWrapper/MPFRUtils.cpp
index 79e7379..314bece 100644
--- a/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/utils/MPFRWrapper/MPFRUtils.cpp
@@ -8,11 +8,10 @@
 
 #include "MPFRUtils.h"
 
+#include "utils/CPP/StringView.h"
 #include "utils/FPUtil/FPBits.h"
 #include "utils/FPUtil/TestHelpers.h"
 
-#include "llvm/ADT/StringRef.h"
-
 #include <memory>
 #include <stdint.h>
 #include <string>
@@ -226,9 +225,9 @@
     constexpr size_t printBufSize = 200;
     char buffer[printBufSize];
     mpfr_snprintf(buffer, printBufSize, "%100.50Rf", value);
-    llvm::StringRef ref(buffer);
-    ref = ref.trim();
-    return ref.str();
+    cpp::StringView view(buffer);
+    view = view.trim(' ');
+    return std::string(view.data());
   }
 
   // These functions are useful for debugging.