diff --git a/libc/test/utils/CPP/CMakeLists.txt b/libc/test/utils/CPP/CMakeLists.txt --- a/libc/test/utils/CPP/CMakeLists.txt +++ b/libc/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/libc/test/utils/CPP/stringview_test.cpp b/libc/test/utils/CPP/stringview_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/utils/CPP/stringview_test.cpp @@ -0,0 +1,105 @@ +//===-- 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("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); +} diff --git a/libc/utils/CPP/CMakeLists.txt b/libc/utils/CPP/CMakeLists.txt --- a/libc/utils/CPP/CMakeLists.txt +++ b/libc/utils/CPP/CMakeLists.txt @@ -5,5 +5,6 @@ ArrayRef.h Bitset.h Functional.h + StringView.h TypeTraits.h ) diff --git a/libc/utils/CPP/StringView.h b/libc/utils/CPP/StringView.h new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/StringView.h @@ -0,0 +1,94 @@ +//===-- 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 + +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(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 { + 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/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/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 #include #include @@ -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.