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 kTheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef ar1(kTheNumbers); + EXPECT_FALSE(ar1.empty()); + EXPECT_EQ(ar1.size(), ARRAY_SIZE(kTheNumbers)); + + ArrayRef ar2(&kTheNumbers[0], &kTheNumbers[2]); + EXPECT_FALSE(ar2.empty()); + EXPECT_EQ(ar2.size(), 2u); + + ArrayRef ar3(&kTheNumbers[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 kTheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef ar1(kTheNumbers); + ArrayRef ar2(kTheNumbers, ar1.size() - 1); + EXPECT_TRUE(ar1.drop_back().equals(ar2)); +} + +TEST(ArrayRefTest, DropFront) { + static const int kTheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef ar1(kTheNumbers); + ArrayRef ar2(&kTheNumbers[2], ar1.size() - 2); + EXPECT_TRUE(ar1.drop_front(2).equals(ar2)); +} + +TEST(ArrayRefTest, TakeBack) { + static const int kTheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef ar1(kTheNumbers); + ArrayRef ar2(ar1.end() - 1, 1); + EXPECT_TRUE(ar1.take_back().equals(ar2)); +} + +TEST(ArrayRefTest, TakeFront) { + static const int kTheNumbers[] = {4, 8, 15, 16, 23, 42}; + ArrayRef ar1(kTheNumbers); + ArrayRef ar2(ar1.data(), 2); + EXPECT_TRUE(ar1.take_front(2).equals(ar2)); +} + +TEST(ArrayRefTest, Equals) { + static const int kA1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + ArrayRef ar1(kA1); + 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 ar1_a = ar1.drop_back(); + EXPECT_TRUE(ar1_a.equals(std::vector({1, 2, 3, 4, 5, 6, 7}))); + EXPECT_FALSE(ar1_a.equals(std::vector({1, 2, 3, 4, 5, 6, 7, 8}))); + + ArrayRef ar1_b = ar1_a.slice(2, 4); + EXPECT_TRUE(ar1_b.equals(std::vector({3, 4, 5, 6}))); + EXPECT_FALSE(ar1_b.equals(std::vector({2, 3, 4, 5, 6}))); + EXPECT_FALSE(ar1_b.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 *ptrs[] = {&buf[0], &buf[1], &buf[2], &buf[3]}; + ArrayRef a((ArrayRef(ptrs))); + a = ArrayRef(ptrs); +} + +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 kA1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // A copy is expected for non-const ArrayRef (thin copy) + ArrayRef ar1(kA1); + const ArrayRef &ar1_ref = ArrayRef(ar1); + EXPECT_NE(&ar1, &ar1_ref); + EXPECT_TRUE(ar1.equals(ar1_ref)); + + // A copy is expected for non-const ArrayRef (thin copy) + const ArrayRef ar2(kA1); + const ArrayRef &ar2_ref = ArrayRef(ar2); + EXPECT_NE(&ar2_ref, &ar2); + EXPECT_TRUE(ar2.equals(ar2_ref)); +} + +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