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 @@ -19,3 +19,13 @@ DEPENDS libc.utils.CPP.standalone_cpp ) + +add_libc_unittest( + arrayref_test + SUITE + libc_cpp_utils_unittests + SRCS + arrayref_test.cpp + DEPENDS + libc.utils.CPP.standalone_cpp +) diff --git a/libc/test/utils/CPP/arrayref_test.cpp b/libc/test/utils/CPP/arrayref_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/utils/CPP/arrayref_test.cpp @@ -0,0 +1,222 @@ +//===-- Unittests for ArrayRef --------------------------------------------===// +// +// 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/ArrayRef.h" +#include "utils/UnitTest/Test.h" + +namespace __llvm_libc { +namespace cpp { + +// The following tests run on both 'ArrayRef' and 'MutableArrayRef'. +using Types = testing::TypeList, MutableArrayRef>; + +TYPED_TEST(LlvmLibcArrayRefTest, ConstructFromElement, Types) { + using value_type = typename ParamType::value_type; + using const_pointer = typename ParamType::const_pointer; + value_type element = 5; + ParamType arrayref(element); + EXPECT_FALSE(arrayref.empty()); + EXPECT_EQ(arrayref.size(), 1UL); + EXPECT_EQ(arrayref[0], 5); + EXPECT_EQ((const_pointer)arrayref.data(), (const_pointer)&element); +} + +TYPED_TEST(LlvmLibcArrayRefTest, ConstructFromPointerAndSize, Types) { + using value_type = typename ParamType::value_type; + using const_pointer = typename ParamType::const_pointer; + value_type values[] = {1, 2}; + ParamType arrayref(values, 2); + EXPECT_FALSE(arrayref.empty()); + EXPECT_EQ(arrayref.size(), 2UL); + EXPECT_EQ(arrayref[0], 1); + EXPECT_EQ(arrayref[1], 2); + EXPECT_EQ((const_pointer)arrayref.data(), (const_pointer)values); +} + +TYPED_TEST(LlvmLibcArrayRefTest, ConstructFromIterator, Types) { + using value_type = typename ParamType::value_type; + using const_pointer = typename ParamType::const_pointer; + value_type values[] = {1, 2}; + ParamType arrayref(&values[0], &values[2]); + EXPECT_FALSE(arrayref.empty()); + EXPECT_EQ(arrayref.size(), 2UL); + EXPECT_EQ(arrayref[0], 1); + EXPECT_EQ(arrayref[1], 2); + EXPECT_EQ((const_pointer)arrayref.data(), (const_pointer)&values[0]); +} + +TYPED_TEST(LlvmLibcArrayRefTest, ConstructFromCArray, Types) { + using value_type = typename ParamType::value_type; + using const_pointer = typename ParamType::const_pointer; + value_type values[] = {1, 2}; + ParamType arrayref(values); + EXPECT_FALSE(arrayref.empty()); + EXPECT_EQ(arrayref.size(), 2UL); + EXPECT_EQ(arrayref[0], 1); + EXPECT_EQ(arrayref[1], 2); + EXPECT_EQ((const_pointer)arrayref.data(), (const_pointer)values); +} + +TYPED_TEST(LlvmLibcArrayRefTest, ConstructFromLibcArray, Types) { + using value_type = typename ParamType::value_type; + using const_pointer = typename ParamType::const_pointer; + Array values = {1, 2}; + ParamType arrayref(values); + EXPECT_FALSE(arrayref.empty()); + EXPECT_EQ(arrayref.size(), 2UL); + EXPECT_EQ(arrayref[0], 1); + EXPECT_EQ(arrayref[1], 2); + EXPECT_EQ((const_pointer)arrayref.data(), (const_pointer)values.data()); +} + +TYPED_TEST(LlvmLibcArrayRefTest, Equals, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType initial(values); + EXPECT_TRUE(initial.equals(initial)); + ParamType shallow_copy(values); + EXPECT_TRUE(initial.equals(shallow_copy)); + value_type same_values[] = {1, 2, 3}; + EXPECT_TRUE(initial.equals(same_values)); + value_type different_values[] = {1, 2, 4}; + EXPECT_FALSE(initial.equals(different_values)); +} + +TYPED_TEST(LlvmLibcArrayRefTest, SliceUnary, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.slice(0).equals(values)); + } + { + value_type values[] = {2, 3}; + EXPECT_TRUE(arrayref.slice(1).equals(values)); + } + { + value_type values[] = {3}; + EXPECT_TRUE(arrayref.slice(2).equals(values)); + } + { EXPECT_TRUE(arrayref.slice(3).empty()); } +} + +TYPED_TEST(LlvmLibcArrayRefTest, SliceBinary, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { + EXPECT_TRUE(arrayref.slice(0, 0).empty()); + EXPECT_TRUE(arrayref.slice(1, 0).empty()); + EXPECT_TRUE(arrayref.slice(2, 0).empty()); + EXPECT_TRUE(arrayref.slice(3, 0).empty()); + } + { + value_type values[] = {1}; + EXPECT_TRUE(arrayref.slice(0, 1).equals(values)); + } + { + value_type values[] = {2}; + EXPECT_TRUE(arrayref.slice(1, 1).equals(values)); + } + { + value_type values[] = {3}; + EXPECT_TRUE(arrayref.slice(2, 1).equals(values)); + } + { + value_type values[] = {1, 2}; + EXPECT_TRUE(arrayref.slice(0, 2).equals(values)); + } + { + value_type values[] = {2, 3}; + EXPECT_TRUE(arrayref.slice(1, 2).equals(values)); + } + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.slice(0, 3).equals(values)); + } +} + +TYPED_TEST(LlvmLibcArrayRefTest, DropFront, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.drop_front(0).equals(values)); + } + { + value_type values[] = {2, 3}; + EXPECT_TRUE(arrayref.drop_front(1).equals(values)); + } + { + value_type values[] = {3}; + EXPECT_TRUE(arrayref.drop_front(2).equals(values)); + } + { EXPECT_TRUE(arrayref.drop_front(3).empty()); } +} + +TYPED_TEST(LlvmLibcArrayRefTest, DropBack, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.drop_back(0).equals(values)); + } + { + value_type values[] = {1, 2}; + EXPECT_TRUE(arrayref.drop_back(1).equals(values)); + } + { + value_type values[] = {1}; + EXPECT_TRUE(arrayref.drop_back(2).equals(values)); + } + { EXPECT_TRUE(arrayref.drop_back(3).empty()); } +} + +TYPED_TEST(LlvmLibcArrayRefTest, TakeFront, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { EXPECT_TRUE(arrayref.take_front(0).empty()); } + { + value_type values[] = {1}; + EXPECT_TRUE(arrayref.take_front(1).equals(values)); + } + { + value_type values[] = {1, 2}; + EXPECT_TRUE(arrayref.take_front(2).equals(values)); + } + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.take_front(3).equals(values)); + } +} + +TYPED_TEST(LlvmLibcArrayRefTest, TakeBack, Types) { + using value_type = typename ParamType::value_type; + value_type values[] = {1, 2, 3}; + ParamType arrayref(values); + { EXPECT_TRUE(arrayref.take_back(0).empty()); } + { + value_type values[] = {3}; + EXPECT_TRUE(arrayref.take_back(1).equals(values)); + } + { + value_type values[] = {2, 3}; + EXPECT_TRUE(arrayref.take_back(2).equals(values)); + } + { + value_type values[] = {1, 2, 3}; + EXPECT_TRUE(arrayref.take_back(3).equals(values)); + } +} + +} // namespace cpp +} // namespace __llvm_libc diff --git a/libc/utils/CPP/ArrayRef.h b/libc/utils/CPP/ArrayRef.h --- a/libc/utils/CPP/ArrayRef.h +++ b/libc/utils/CPP/ArrayRef.h @@ -10,6 +10,7 @@ #define LLVM_LIBC_UTILS_CPP_ARRAYREF_H #include "Array.h" +#include "TypeTraits.h" //RemoveCVType #include // For size_t. @@ -21,81 +22,116 @@ // llvm/ADT/ArrayRef.h. The implementations in this file are of a limited // functionality, but can be extended in an as needed basis. namespace internal { -template class ArrayRefBase { +template class ArrayRefBase { public: - using iterator = T *; - using pointer = T *; - using reference = T &; + using value_type = RemoveCVType; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = const_pointer; + using const_iterator = const_pointer; + using size_type = size_t; + using difference_type = ptrdiff_t; ArrayRefBase() = default; - // From Array. - template - ArrayRefBase(Array &Arr) : Data(Arr.Data), Length(N) {} - // Construct an ArrayRefBase from a single element. - explicit ArrayRefBase(T &OneElt) : Data(&OneElt), Length(1) {} + explicit ArrayRefBase(QualifiedT &OneElt) : Data(&OneElt), Length(1) {} // Construct an ArrayRefBase from a pointer and length. - ArrayRefBase(pointer Data, size_t Length) : Data(Data), Length(Length) {} + ArrayRefBase(QualifiedT *Data, size_t Length) : Data(Data), Length(Length) {} // Construct an ArrayRefBase from a range. - ArrayRefBase(iterator Begin, iterator End) + ArrayRefBase(QualifiedT *Begin, QualifiedT *End) : Data(Begin), Length(End - Begin) {} // Construct an ArrayRefBase from a C array. template - constexpr ArrayRefBase(T (&Arr)[N]) : Data(Arr), Length(N) {} + constexpr ArrayRefBase(QualifiedT (&Arr)[N]) : Data(Arr), Length(N) {} - iterator begin() const { return Data; } - iterator end() const { return Data + Length; } - - bool empty() const { return Length == 0; } + QualifiedT *data() const { return Data; } + size_t size() const { return Length; } - pointer data() const { return Data; } + auto begin() const { return data(); } + auto end() const { return data() + size(); } - size_t size() const { return Length; } + bool empty() const { return size() == 0; } - reference operator[](size_t Index) const { return Data[Index]; } + auto operator[](size_t Index) const { return data()[Index]; } // slice(n, m) - Chop off the first N elements of the array, and keep M // elements in the array. - ArrayRefBase slice(size_t N, size_t M) const { - return ArrayRefBase(data() + N, M); - } + auto slice(size_t N, size_t M) const { return ArrayRefBase(data() + N, M); } // slice(n) - Chop off the first N elements of the array. - ArrayRefBase slice(size_t N) const { return slice(N, size() - N); } + auto slice(size_t N) const { return slice(N, size() - N); } // Drop the first \p N elements of the array. - ArrayRefBase drop_front(size_t N = 1) const { - return slice(N, size() - N); - } + auto drop_front(size_t N = 1) const { return slice(N, size() - N); } // Drop the last \p N elements of the array. - ArrayRefBase drop_back(size_t N = 1) const { return slice(0, size() - N); } + auto drop_back(size_t N = 1) const { return slice(0, size() - N); } // Return a copy of *this with only the first \p N elements. - ArrayRefBase take_front(size_t N = 1) const { + auto take_front(size_t N = 1) const { if (N >= size()) return *this; return drop_back(size() - N); } // Return a copy of *this with only the last \p N elements. - ArrayRefBase take_back(size_t N = 1) const { + auto take_back(size_t N = 1) const { if (N >= size()) return *this; return drop_front(size() - N); } + // equals - Check for element-wise equality. + bool equals(ArrayRefBase RHS) const { + if (Length != RHS.Length) + return false; + auto First1 = begin(); + auto Last1 = end(); + auto First2 = RHS.begin(); + for (; First1 != Last1; ++First1, ++First2) { + if (!(*First1 == *First2)) { + return false; + } + } + return true; + } + private: - pointer Data = nullptr; + QualifiedT *Data = nullptr; size_t Length = 0; }; } // namespace internal -template using ArrayRef = internal::ArrayRefBase; -template using MutableArrayRef = internal::ArrayRefBase; +template struct ArrayRef : public internal::ArrayRefBase { +private: + static_assert(IsSameV>, + "ArrayRef must have a non-const, non-volatile value_type"); + using Impl = internal::ArrayRefBase; + using Impl::Impl; + +public: + // From Array. + template ArrayRef(const Array &Arr) : Impl(Arr.Data, N) {} +}; + +template +struct MutableArrayRef : public internal::ArrayRefBase { +private: + static_assert( + IsSameV>, + "MutableArrayRef must have a non-const, non-volatile value_type"); + using Impl = internal::ArrayRefBase; + using Impl::Impl; + +public: + // From Array. + template MutableArrayRef(Array &Arr) : Impl(Arr.Data, N) {} +}; } // namespace cpp } // namespace __llvm_libc