diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_array_ref.h b/compiler-rt/lib/sanitizer_common/sanitizer_array_ref.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_array_ref.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_array_ref.h @@ -27,11 +27,20 @@ template class ArrayRef { public: - ArrayRef() {} - ArrayRef(const T *begin, const T *end) : begin_(begin), end_(end) {} - + constexpr ArrayRef() {} + constexpr ArrayRef(const T *begin, const T *end) : begin_(begin), end_(end) { + DCHECK(empty() || begin); + } + constexpr ArrayRef(const T *data, uptr length) + : ArrayRef(data, data + length) {} + template + constexpr ArrayRef(const T (&src)[N]) : ArrayRef(src, src + N) {} template - ArrayRef(const C &src) : ArrayRef(src.data(), src.data() + src.size()) {} + constexpr ArrayRef(const C &src) + : ArrayRef(src.data(), src.data() + src.size()) {} + ArrayRef(const T &one_elt) : ArrayRef(&one_elt, &one_elt + 1) {} + + const T *data() const { return empty() ? nullptr : begin_; } const T *begin() const { return begin_; } const T *end() const { return end_; } @@ -40,11 +49,75 @@ uptr size() const { return end_ - begin_; } + /// equals - Check for element-wise equality. + bool equals(ArrayRef rhs) const { + if (size() != rhs.size()) + return false; + auto r = rhs.begin(); + for (auto &l : *this) { + if (!(l == *r)) + return false; + ++r; + } + return true; + } + + /// slice(n, m) - Chop off the first N elements of the array, and keep M + /// elements in the array. + ArrayRef slice(uptr N, uptr M) const { + DCHECK_LE(N + M, size()); + return ArrayRef(data() + N, M); + } + + /// slice(n) - Chop off the first N elements of the array. + ArrayRef slice(uptr N) const { return slice(N, size() - N); } + + /// Drop the first \p N elements of the array. + ArrayRef drop_front(uptr N = 1) const { + DCHECK_GE(size(), N); + return slice(N, size() - N); + } + + /// Drop the last \p N elements of the array. + ArrayRef drop_back(uptr N = 1) const { + DCHECK_GE(size(), N); + return slice(0, size() - N); + } + + /// Return a copy of *this with only the first \p N elements. + ArrayRef take_front(uptr 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. + ArrayRef take_back(uptr N = 1) const { + if (N >= size()) + return *this; + return drop_front(size() - N); + } + + const T &operator[](uptr index) const { + DCHECK_LT(index, size()); + return begin_[index]; + } + private: const T *begin_ = nullptr; const T *end_ = nullptr; }; +template +inline bool operator==(ArrayRef lhs, ArrayRef rhs) { + return lhs.equals(rhs); +} + +template +inline bool operator!=(ArrayRef lhs, ArrayRef rhs) { + return !(lhs == rhs); +} + } // namespace __sanitizer #endif // SANITIZER_ARRAY_REF_H diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt --- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(SANITIZER_UNITTESTS sanitizer_addrhashmap_test.cpp sanitizer_allocator_test.cpp + sanitizer_array_ref_test.cpp sanitizer_atomic_test.cpp sanitizer_bitvector_test.cpp sanitizer_bvgraph_test.cpp diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_array_ref_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_array_ref_test.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_array_ref_test.cpp @@ -0,0 +1,148 @@ +//===- sanitizer_array_ref.cpp - ArrayRef unit tests ----------------------===// +// +// 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 "sanitizer_common/sanitizer_array_ref.h" + +#include + +#include "gtest/gtest.h" +#include "sanitizer_internal_defs.h" + +using namespace __sanitizer; +namespace { + +TEST(ArrayRefTest, Constructors) { + ArrayRef AR0; + EXPECT_TRUE(AR0.empty()); + EXPECT_EQ(AR0.size(), 0u); + + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + EXPECT_FALSE(AR1.empty()); + EXPECT_EQ(AR1.size(), ARRAY_SIZE(TheNumbers)); + + ArrayRef AR2(&TheNumbers[0], &TheNumbers[2]); + EXPECT_FALSE(AR2.empty()); + EXPECT_EQ(AR2.size(), 2u); + + ArrayRef AR3(&TheNumbers[0], 3); + EXPECT_FALSE(AR3.empty()); + EXPECT_EQ(AR3.size(), 3u); + + std::vector v(4, 1); + ArrayRef AR4(v); + EXPECT_FALSE(AR4.empty()); + EXPECT_EQ(AR4.size(), 4u); + + int n; + ArrayRef AR5(n); + EXPECT_FALSE(AR5.empty()); + EXPECT_EQ(AR5.size(), 1u); +} + +TEST(ArrayRefTest, DropBack) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(TheNumbers, AR1.size() - 1); + EXPECT_TRUE(AR1.drop_back().equals(AR2)); +} + +TEST(ArrayRefTest, DropFront) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(&TheNumbers[2], AR1.size() - 2); + EXPECT_TRUE(AR1.drop_front(2).equals(AR2)); +} + +TEST(ArrayRefTest, TakeBack) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(AR1.end() - 1, 1); + EXPECT_TRUE(AR1.take_back().equals(AR2)); +} + +TEST(ArrayRefTest, TakeFront) { + static const int TheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef AR1(TheNumbers); + ArrayRef AR2(AR1.data(), 2); + EXPECT_TRUE(AR1.take_front(2).equals(AR2)); +} + +TEST(ArrayRefTest, Equals) { + static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + ArrayRef AR1(A1); + EXPECT_TRUE(AR1.equals(std::vector({1, 2, 3, 4, 5, 6, 7, 8}))); + EXPECT_FALSE(AR1.equals(std::vector({8, 1, 2, 4, 5, 6, 6, 7}))); + EXPECT_FALSE(AR1.equals(std::vector({2, 4, 5, 6, 6, 7, 8, 1}))); + EXPECT_FALSE(AR1.equals(std::vector({0, 1, 2, 4, 5, 6, 6, 7}))); + EXPECT_FALSE(AR1.equals(std::vector({1, 2, 42, 4, 5, 6, 7, 8}))); + EXPECT_FALSE(AR1.equals(std::vector({42, 2, 3, 4, 5, 6, 7, 8}))); + EXPECT_FALSE(AR1.equals(std::vector({1, 2, 3, 4, 5, 6, 7, 42}))); + EXPECT_FALSE(AR1.equals(std::vector({1, 2, 3, 4, 5, 6, 7}))); + EXPECT_FALSE(AR1.equals(std::vector({1, 2, 3, 4, 5, 6, 7, 8, 9}))); + + ArrayRef AR1a = AR1.drop_back(); + EXPECT_TRUE(AR1a.equals(std::vector({1, 2, 3, 4, 5, 6, 7}))); + EXPECT_FALSE(AR1a.equals(std::vector({1, 2, 3, 4, 5, 6, 7, 8}))); + + ArrayRef AR1b = AR1a.slice(2, 4); + EXPECT_TRUE(AR1b.equals(std::vector({3, 4, 5, 6}))); + EXPECT_FALSE(AR1b.equals(std::vector({2, 3, 4, 5, 6}))); + EXPECT_FALSE(AR1b.equals(std::vector({3, 4, 5, 6, 7}))); +} + +TEST(ArrayRefTest, EmptyEquals) { + EXPECT_TRUE(ArrayRef() == ArrayRef()); +} + +TEST(ArrayRefTest, ConstConvert) { + int buf[4]; + for (int i = 0; i < 4; ++i) buf[i] = i; + + static int *A[] = {&buf[0], &buf[1], &buf[2], &buf[3]}; + ArrayRef a((ArrayRef(A))); + a = ArrayRef(A); +} + +static std::vector ReturnTest12() { return {1, 2}; } +static void ArgTest12(ArrayRef A) { + EXPECT_EQ(2U, A.size()); + EXPECT_EQ(1, A[0]); + EXPECT_EQ(2, A[1]); +} + +TEST(ArrayRefTest, ArrayRef) { + static const int A1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // A copy is expected for non-const ArrayRef (thin copy) + ArrayRef AR1(A1); + const ArrayRef &AR1Ref = ArrayRef(AR1); + EXPECT_NE(&AR1, &AR1Ref); + EXPECT_TRUE(AR1.equals(AR1Ref)); + + // A copy is expected for non-const ArrayRef (thin copy) + const ArrayRef AR2(A1); + const ArrayRef &AR2Ref = ArrayRef(AR2); + EXPECT_NE(&AR2Ref, &AR2); + EXPECT_TRUE(AR2.equals(AR2Ref)); +} + +TEST(ArrayRefTest, ArrayRefFromStdArray) { + std::array A1{{42, -5, 0, 1000000, -1000000}}; + ArrayRef A2 = ArrayRef(A1); + + EXPECT_EQ(A1.size(), A2.size()); + for (std::size_t i = 0; i < A1.size(); ++i) { + EXPECT_EQ(A1[i], A2[i]); + } +} + +static_assert(std::is_trivially_copyable_v>, + "trivially copyable"); + +} // namespace