| // Copyright 2017 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "absl/container/fixed_array.h" | |
| #include <stdio.h> | |
| #include <list> | |
| #include <memory> | |
| #include <numeric> | |
| #include <stdexcept> | |
| #include <string> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "absl/base/internal/exception_testing.h" | |
| #include "absl/memory/memory.h" | |
| using ::testing::ElementsAreArray; | |
| namespace { | |
| // Helper routine to determine if a absl::FixedArray used stack allocation. | |
| template <typename ArrayType> | |
| static bool IsOnStack(const ArrayType& a) { | |
| return a.size() <= ArrayType::inline_elements; | |
| } | |
| class ConstructionTester { | |
| public: | |
| ConstructionTester() | |
| : self_ptr_(this), | |
| value_(0) { | |
| constructions++; | |
| } | |
| ~ConstructionTester() { | |
| assert(self_ptr_ == this); | |
| self_ptr_ = nullptr; | |
| destructions++; | |
| } | |
| // These are incremented as elements are constructed and destructed so we can | |
| // be sure all elements are properly cleaned up. | |
| static int constructions; | |
| static int destructions; | |
| void CheckConstructed() { | |
| assert(self_ptr_ == this); | |
| } | |
| void set(int value) { value_ = value; } | |
| int get() { return value_; } | |
| private: | |
| // self_ptr_ should always point to 'this' -- that's how we can be sure the | |
| // constructor has been called. | |
| ConstructionTester* self_ptr_; | |
| int value_; | |
| }; | |
| int ConstructionTester::constructions = 0; | |
| int ConstructionTester::destructions = 0; | |
| // ThreeInts will initialize its three ints to the value stored in | |
| // ThreeInts::counter. The constructor increments counter so that each object | |
| // in an array of ThreeInts will have different values. | |
| class ThreeInts { | |
| public: | |
| ThreeInts() { | |
| x_ = counter; | |
| y_ = counter; | |
| z_ = counter; | |
| ++counter; | |
| } | |
| static int counter; | |
| int x_, y_, z_; | |
| }; | |
| int ThreeInts::counter = 0; | |
| TEST(FixedArrayTest, CopyCtor) { | |
| absl::FixedArray<int, 10> on_stack(5); | |
| std::iota(on_stack.begin(), on_stack.end(), 0); | |
| absl::FixedArray<int, 10> stack_copy = on_stack; | |
| EXPECT_THAT(stack_copy, ElementsAreArray(on_stack)); | |
| EXPECT_TRUE(IsOnStack(stack_copy)); | |
| absl::FixedArray<int, 10> allocated(15); | |
| std::iota(allocated.begin(), allocated.end(), 0); | |
| absl::FixedArray<int, 10> alloced_copy = allocated; | |
| EXPECT_THAT(alloced_copy, ElementsAreArray(allocated)); | |
| EXPECT_FALSE(IsOnStack(alloced_copy)); | |
| } | |
| TEST(FixedArrayTest, MoveCtor) { | |
| absl::FixedArray<std::unique_ptr<int>, 10> on_stack(5); | |
| for (int i = 0; i < 5; ++i) { | |
| on_stack[i] = absl::make_unique<int>(i); | |
| } | |
| absl::FixedArray<std::unique_ptr<int>, 10> stack_copy = std::move(on_stack); | |
| for (int i = 0; i < 5; ++i) EXPECT_EQ(*(stack_copy[i]), i); | |
| EXPECT_EQ(stack_copy.size(), on_stack.size()); | |
| absl::FixedArray<std::unique_ptr<int>, 10> allocated(15); | |
| for (int i = 0; i < 15; ++i) { | |
| allocated[i] = absl::make_unique<int>(i); | |
| } | |
| absl::FixedArray<std::unique_ptr<int>, 10> alloced_copy = | |
| std::move(allocated); | |
| for (int i = 0; i < 15; ++i) EXPECT_EQ(*(alloced_copy[i]), i); | |
| EXPECT_EQ(allocated.size(), alloced_copy.size()); | |
| } | |
| TEST(FixedArrayTest, SmallObjects) { | |
| // Small object arrays | |
| { | |
| // Short arrays should be on the stack | |
| absl::FixedArray<int> array(4); | |
| EXPECT_TRUE(IsOnStack(array)); | |
| } | |
| { | |
| // Large arrays should be on the heap | |
| absl::FixedArray<int> array(1048576); | |
| EXPECT_FALSE(IsOnStack(array)); | |
| } | |
| { | |
| // Arrays of <= default size should be on the stack | |
| absl::FixedArray<int, 100> array(100); | |
| EXPECT_TRUE(IsOnStack(array)); | |
| } | |
| { | |
| // Arrays of > default size should be on the stack | |
| absl::FixedArray<int, 100> array(101); | |
| EXPECT_FALSE(IsOnStack(array)); | |
| } | |
| { | |
| // Arrays with different size elements should use approximately | |
| // same amount of stack space | |
| absl::FixedArray<int> array1(0); | |
| absl::FixedArray<char> array2(0); | |
| EXPECT_LE(sizeof(array1), sizeof(array2)+100); | |
| EXPECT_LE(sizeof(array2), sizeof(array1)+100); | |
| } | |
| { | |
| // Ensure that vectors are properly constructed inside a fixed array. | |
| absl::FixedArray<std::vector<int> > array(2); | |
| EXPECT_EQ(0, array[0].size()); | |
| EXPECT_EQ(0, array[1].size()); | |
| } | |
| { | |
| // Regardless of absl::FixedArray implementation, check that a type with a | |
| // low alignment requirement and a non power-of-two size is initialized | |
| // correctly. | |
| ThreeInts::counter = 1; | |
| absl::FixedArray<ThreeInts> array(2); | |
| EXPECT_EQ(1, array[0].x_); | |
| EXPECT_EQ(1, array[0].y_); | |
| EXPECT_EQ(1, array[0].z_); | |
| EXPECT_EQ(2, array[1].x_); | |
| EXPECT_EQ(2, array[1].y_); | |
| EXPECT_EQ(2, array[1].z_); | |
| } | |
| } | |
| TEST(FixedArrayTest, AtThrows) { | |
| absl::FixedArray<int> a = {1, 2, 3}; | |
| EXPECT_EQ(a.at(2), 3); | |
| ABSL_BASE_INTERNAL_EXPECT_FAIL(a.at(3), std::out_of_range, | |
| "failed bounds check"); | |
| } | |
| TEST(FixedArrayRelationalsTest, EqualArrays) { | |
| for (int i = 0; i < 10; ++i) { | |
| absl::FixedArray<int, 5> a1(i); | |
| std::iota(a1.begin(), a1.end(), 0); | |
| absl::FixedArray<int, 5> a2(a1.begin(), a1.end()); | |
| EXPECT_TRUE(a1 == a2); | |
| EXPECT_FALSE(a1 != a2); | |
| EXPECT_TRUE(a2 == a1); | |
| EXPECT_FALSE(a2 != a1); | |
| EXPECT_FALSE(a1 < a2); | |
| EXPECT_FALSE(a1 > a2); | |
| EXPECT_FALSE(a2 < a1); | |
| EXPECT_FALSE(a2 > a1); | |
| EXPECT_TRUE(a1 <= a2); | |
| EXPECT_TRUE(a1 >= a2); | |
| EXPECT_TRUE(a2 <= a1); | |
| EXPECT_TRUE(a2 >= a1); | |
| } | |
| } | |
| TEST(FixedArrayRelationalsTest, UnequalArrays) { | |
| for (int i = 1; i < 10; ++i) { | |
| absl::FixedArray<int, 5> a1(i); | |
| std::iota(a1.begin(), a1.end(), 0); | |
| absl::FixedArray<int, 5> a2(a1.begin(), a1.end()); | |
| --a2[i / 2]; | |
| EXPECT_FALSE(a1 == a2); | |
| EXPECT_TRUE(a1 != a2); | |
| EXPECT_FALSE(a2 == a1); | |
| EXPECT_TRUE(a2 != a1); | |
| EXPECT_FALSE(a1 < a2); | |
| EXPECT_TRUE(a1 > a2); | |
| EXPECT_TRUE(a2 < a1); | |
| EXPECT_FALSE(a2 > a1); | |
| EXPECT_FALSE(a1 <= a2); | |
| EXPECT_TRUE(a1 >= a2); | |
| EXPECT_TRUE(a2 <= a1); | |
| EXPECT_FALSE(a2 >= a1); | |
| } | |
| } | |
| template <int stack_elements> | |
| static void TestArray(int n) { | |
| SCOPED_TRACE(n); | |
| SCOPED_TRACE(stack_elements); | |
| ConstructionTester::constructions = 0; | |
| ConstructionTester::destructions = 0; | |
| { | |
| absl::FixedArray<ConstructionTester, stack_elements> array(n); | |
| EXPECT_THAT(array.size(), n); | |
| EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n); | |
| EXPECT_THAT(array.begin() + n, array.end()); | |
| // Check that all elements were constructed | |
| for (int i = 0; i < n; i++) { | |
| array[i].CheckConstructed(); | |
| } | |
| // Check that no other elements were constructed | |
| EXPECT_THAT(ConstructionTester::constructions, n); | |
| // Test operator[] | |
| for (int i = 0; i < n; i++) { | |
| array[i].set(i); | |
| } | |
| for (int i = 0; i < n; i++) { | |
| EXPECT_THAT(array[i].get(), i); | |
| EXPECT_THAT(array.data()[i].get(), i); | |
| } | |
| // Test data() | |
| for (int i = 0; i < n; i++) { | |
| array.data()[i].set(i + 1); | |
| } | |
| for (int i = 0; i < n; i++) { | |
| EXPECT_THAT(array[i].get(), i+1); | |
| EXPECT_THAT(array.data()[i].get(), i+1); | |
| } | |
| } // Close scope containing 'array'. | |
| // Check that all constructed elements were destructed. | |
| EXPECT_EQ(ConstructionTester::constructions, | |
| ConstructionTester::destructions); | |
| } | |
| template <int elements_per_inner_array, int inline_elements> | |
| static void TestArrayOfArrays(int n) { | |
| SCOPED_TRACE(n); | |
| SCOPED_TRACE(inline_elements); | |
| SCOPED_TRACE(elements_per_inner_array); | |
| ConstructionTester::constructions = 0; | |
| ConstructionTester::destructions = 0; | |
| { | |
| using InnerArray = ConstructionTester[elements_per_inner_array]; | |
| // Heap-allocate the FixedArray to avoid blowing the stack frame. | |
| auto array_ptr = | |
| absl::make_unique<absl::FixedArray<InnerArray, inline_elements>>(n); | |
| auto& array = *array_ptr; | |
| ASSERT_EQ(array.size(), n); | |
| ASSERT_EQ(array.memsize(), | |
| sizeof(ConstructionTester) * elements_per_inner_array * n); | |
| ASSERT_EQ(array.begin() + n, array.end()); | |
| // Check that all elements were constructed | |
| for (int i = 0; i < n; i++) { | |
| for (int j = 0; j < elements_per_inner_array; j++) { | |
| (array[i])[j].CheckConstructed(); | |
| } | |
| } | |
| // Check that no other elements were constructed | |
| ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array); | |
| // Test operator[] | |
| for (int i = 0; i < n; i++) { | |
| for (int j = 0; j < elements_per_inner_array; j++) { | |
| (array[i])[j].set(i * elements_per_inner_array + j); | |
| } | |
| } | |
| for (int i = 0; i < n; i++) { | |
| for (int j = 0; j < elements_per_inner_array; j++) { | |
| ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j); | |
| ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j); | |
| } | |
| } | |
| // Test data() | |
| for (int i = 0; i < n; i++) { | |
| for (int j = 0; j < elements_per_inner_array; j++) { | |
| (array.data()[i])[j].set((i + 1) * elements_per_inner_array + j); | |
| } | |
| } | |
| for (int i = 0; i < n; i++) { | |
| for (int j = 0; j < elements_per_inner_array; j++) { | |
| ASSERT_EQ((array[i])[j].get(), | |
| (i + 1) * elements_per_inner_array + j); | |
| ASSERT_EQ((array.data()[i])[j].get(), | |
| (i + 1) * elements_per_inner_array + j); | |
| } | |
| } | |
| } // Close scope containing 'array'. | |
| // Check that all constructed elements were destructed. | |
| EXPECT_EQ(ConstructionTester::constructions, | |
| ConstructionTester::destructions); | |
| } | |
| TEST(IteratorConstructorTest, NonInline) { | |
| int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; | |
| absl::FixedArray<int, ABSL_ARRAYSIZE(kInput) - 1> const fixed( | |
| kInput, kInput + ABSL_ARRAYSIZE(kInput)); | |
| ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); | |
| for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) { | |
| ASSERT_EQ(kInput[i], fixed[i]); | |
| } | |
| } | |
| TEST(IteratorConstructorTest, Inline) { | |
| int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; | |
| absl::FixedArray<int, ABSL_ARRAYSIZE(kInput)> const fixed( | |
| kInput, kInput + ABSL_ARRAYSIZE(kInput)); | |
| ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); | |
| for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) { | |
| ASSERT_EQ(kInput[i], fixed[i]); | |
| } | |
| } | |
| TEST(IteratorConstructorTest, NonPod) { | |
| char const* kInput[] = | |
| { "red", "orange", "yellow", "green", "blue", "indigo", "violet" }; | |
| absl::FixedArray<std::string> const fixed(kInput, kInput + ABSL_ARRAYSIZE(kInput)); | |
| ASSERT_EQ(ABSL_ARRAYSIZE(kInput), fixed.size()); | |
| for (size_t i = 0; i < ABSL_ARRAYSIZE(kInput); ++i) { | |
| ASSERT_EQ(kInput[i], fixed[i]); | |
| } | |
| } | |
| TEST(IteratorConstructorTest, FromEmptyVector) { | |
| std::vector<int> const empty; | |
| absl::FixedArray<int> const fixed(empty.begin(), empty.end()); | |
| EXPECT_EQ(0, fixed.size()); | |
| EXPECT_EQ(empty.size(), fixed.size()); | |
| } | |
| TEST(IteratorConstructorTest, FromNonEmptyVector) { | |
| int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; | |
| std::vector<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); | |
| absl::FixedArray<int> const fixed(items.begin(), items.end()); | |
| ASSERT_EQ(items.size(), fixed.size()); | |
| for (size_t i = 0; i < items.size(); ++i) { | |
| ASSERT_EQ(items[i], fixed[i]); | |
| } | |
| } | |
| TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) { | |
| int const kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; | |
| std::list<int> const items(kInput, kInput + ABSL_ARRAYSIZE(kInput)); | |
| absl::FixedArray<int> const fixed(items.begin(), items.end()); | |
| EXPECT_THAT(fixed, testing::ElementsAreArray(kInput)); | |
| } | |
| TEST(InitListConstructorTest, InitListConstruction) { | |
| absl::FixedArray<int> fixed = {1, 2, 3}; | |
| EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3})); | |
| } | |
| TEST(FillConstructorTest, NonEmptyArrays) { | |
| absl::FixedArray<int> stack_array(4, 1); | |
| EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1})); | |
| absl::FixedArray<int, 0> heap_array(4, 1); | |
| EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1})); | |
| } | |
| TEST(FillConstructorTest, EmptyArray) { | |
| absl::FixedArray<int> empty_fill(0, 1); | |
| absl::FixedArray<int> empty_size(0); | |
| EXPECT_EQ(empty_fill, empty_size); | |
| } | |
| TEST(FillConstructorTest, NotTriviallyCopyable) { | |
| std::string str = "abcd"; | |
| absl::FixedArray<std::string> strings = {str, str, str, str}; | |
| absl::FixedArray<std::string> array(4, str); | |
| EXPECT_EQ(array, strings); | |
| } | |
| TEST(FillConstructorTest, Disambiguation) { | |
| absl::FixedArray<size_t> a(1, 2); | |
| EXPECT_THAT(a, testing::ElementsAre(2)); | |
| } | |
| TEST(FixedArrayTest, ManySizedArrays) { | |
| std::vector<int> sizes; | |
| for (int i = 1; i < 100; i++) sizes.push_back(i); | |
| for (int i = 100; i <= 1000; i += 100) sizes.push_back(i); | |
| for (int n : sizes) { | |
| TestArray<0>(n); | |
| TestArray<1>(n); | |
| TestArray<64>(n); | |
| TestArray<1000>(n); | |
| } | |
| } | |
| TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) { | |
| for (int n = 1; n < 1000; n++) { | |
| ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n))); | |
| ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n))); | |
| ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n))); | |
| ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n))); | |
| } | |
| } | |
| TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) { | |
| for (int n = 1; n < 1000; n++) { | |
| TestArrayOfArrays<2, 0>(n); | |
| TestArrayOfArrays<2, 1>(n); | |
| TestArrayOfArrays<2, 64>(n); | |
| TestArrayOfArrays<2, 1000>(n); | |
| } | |
| } | |
| // If value_type is put inside of a struct container, | |
| // we might evoke this error in a hardened build unless data() is carefully | |
| // written, so check on that. | |
| // error: call to int __builtin___sprintf_chk(etc...) | |
| // will always overflow destination buffer [-Werror] | |
| TEST(FixedArrayTest, AvoidParanoidDiagnostics) { | |
| absl::FixedArray<char, 32> buf(32); | |
| sprintf(buf.data(), "foo"); // NOLINT(runtime/printf) | |
| } | |
| TEST(FixedArrayTest, TooBigInlinedSpace) { | |
| struct TooBig { | |
| char c[1 << 20]; | |
| }; // too big for even one on the stack | |
| // Simulate the data members of absl::FixedArray, a pointer and a size_t. | |
| struct Data { | |
| TooBig* p; | |
| size_t size; | |
| }; | |
| // Make sure TooBig objects are not inlined for 0 or default size. | |
| static_assert(sizeof(absl::FixedArray<TooBig, 0>) == sizeof(Data), | |
| "0-sized absl::FixedArray should have same size as Data."); | |
| static_assert(alignof(absl::FixedArray<TooBig, 0>) == alignof(Data), | |
| "0-sized absl::FixedArray should have same alignment as Data."); | |
| static_assert(sizeof(absl::FixedArray<TooBig>) == sizeof(Data), | |
| "default-sized absl::FixedArray should have same size as Data"); | |
| static_assert( | |
| alignof(absl::FixedArray<TooBig>) == alignof(Data), | |
| "default-sized absl::FixedArray should have same alignment as Data."); | |
| } | |
| // PickyDelete EXPECTs its class-scope deallocation funcs are unused. | |
| struct PickyDelete { | |
| PickyDelete() {} | |
| ~PickyDelete() {} | |
| void operator delete(void* p) { | |
| EXPECT_TRUE(false) << __FUNCTION__; | |
| ::operator delete(p); | |
| } | |
| void operator delete[](void* p) { | |
| EXPECT_TRUE(false) << __FUNCTION__; | |
| ::operator delete[](p); | |
| } | |
| }; | |
| TEST(FixedArrayTest, UsesGlobalAlloc) { absl::FixedArray<PickyDelete, 0> a(5); } | |
| TEST(FixedArrayTest, Data) { | |
| static const int kInput[] = { 2, 3, 5, 7, 11, 13, 17 }; | |
| absl::FixedArray<int> fa(std::begin(kInput), std::end(kInput)); | |
| EXPECT_EQ(fa.data(), &*fa.begin()); | |
| EXPECT_EQ(fa.data(), &fa[0]); | |
| const absl::FixedArray<int>& cfa = fa; | |
| EXPECT_EQ(cfa.data(), &*cfa.begin()); | |
| EXPECT_EQ(cfa.data(), &cfa[0]); | |
| } | |
| TEST(FixedArrayTest, Empty) { | |
| absl::FixedArray<int> empty(0); | |
| absl::FixedArray<int> inline_filled(1); | |
| absl::FixedArray<int, 0> heap_filled(1); | |
| EXPECT_TRUE(empty.empty()); | |
| EXPECT_FALSE(inline_filled.empty()); | |
| EXPECT_FALSE(heap_filled.empty()); | |
| } | |
| TEST(FixedArrayTest, FrontAndBack) { | |
| absl::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3}; | |
| EXPECT_EQ(inlined.front(), 1); | |
| EXPECT_EQ(inlined.back(), 3); | |
| absl::FixedArray<int, 0> allocated = {1, 2, 3}; | |
| EXPECT_EQ(allocated.front(), 1); | |
| EXPECT_EQ(allocated.back(), 3); | |
| absl::FixedArray<int> one_element = {1}; | |
| EXPECT_EQ(one_element.front(), one_element.back()); | |
| } | |
| TEST(FixedArrayTest, ReverseIteratorInlined) { | |
| absl::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4}; | |
| int counter = 5; | |
| for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin(); | |
| iter != a.rend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| counter = 5; | |
| for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin(); | |
| iter != a.rend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| counter = 5; | |
| for (auto iter = a.crbegin(); iter != a.crend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| } | |
| TEST(FixedArrayTest, ReverseIteratorAllocated) { | |
| absl::FixedArray<int, 0> a = {0, 1, 2, 3, 4}; | |
| int counter = 5; | |
| for (absl::FixedArray<int>::reverse_iterator iter = a.rbegin(); | |
| iter != a.rend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| counter = 5; | |
| for (absl::FixedArray<int>::const_reverse_iterator iter = a.rbegin(); | |
| iter != a.rend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| counter = 5; | |
| for (auto iter = a.crbegin(); iter != a.crend(); ++iter) { | |
| counter--; | |
| EXPECT_EQ(counter, *iter); | |
| } | |
| EXPECT_EQ(counter, 0); | |
| } | |
| TEST(FixedArrayTest, Fill) { | |
| absl::FixedArray<int, 5 * sizeof(int)> inlined(5); | |
| int fill_val = 42; | |
| inlined.fill(fill_val); | |
| for (int i : inlined) EXPECT_EQ(i, fill_val); | |
| absl::FixedArray<int, 0> allocated(5); | |
| allocated.fill(fill_val); | |
| for (int i : allocated) EXPECT_EQ(i, fill_val); | |
| // It doesn't do anything, just make sure this compiles. | |
| absl::FixedArray<int> empty(0); | |
| empty.fill(fill_val); | |
| } | |
| #ifdef ADDRESS_SANITIZER | |
| TEST(FixedArrayTest, AddressSanitizerAnnotations1) { | |
| absl::FixedArray<int, 32> a(10); | |
| int *raw = a.data(); | |
| raw[0] = 0; | |
| raw[9] = 0; | |
| EXPECT_DEATH(raw[-2] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[-1] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[10] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[31] = 0, "container-overflow"); | |
| } | |
| TEST(FixedArrayTest, AddressSanitizerAnnotations2) { | |
| absl::FixedArray<char, 17> a(12); | |
| char *raw = a.data(); | |
| raw[0] = 0; | |
| raw[11] = 0; | |
| EXPECT_DEATH(raw[-7] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[-1] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[12] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[17] = 0, "container-overflow"); | |
| } | |
| TEST(FixedArrayTest, AddressSanitizerAnnotations3) { | |
| absl::FixedArray<uint64_t, 20> a(20); | |
| uint64_t *raw = a.data(); | |
| raw[0] = 0; | |
| raw[19] = 0; | |
| EXPECT_DEATH(raw[-1] = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[20] = 0, "container-overflow"); | |
| } | |
| TEST(FixedArrayTest, AddressSanitizerAnnotations4) { | |
| absl::FixedArray<ThreeInts> a(10); | |
| ThreeInts *raw = a.data(); | |
| raw[0] = ThreeInts(); | |
| raw[9] = ThreeInts(); | |
| // Note: raw[-1] is pointing to 12 bytes before the container range. However, | |
| // there is only a 8-byte red zone before the container range, so we only | |
| // access the last 4 bytes of the struct to make sure it stays within the red | |
| // zone. | |
| EXPECT_DEATH(raw[-1].z_ = 0, "container-overflow"); | |
| EXPECT_DEATH(raw[10] = ThreeInts(), "container-overflow"); | |
| // The actual size of storage is kDefaultBytes=256, 21*12 = 252, | |
| // so reading raw[21] should still trigger the correct warning. | |
| EXPECT_DEATH(raw[21] = ThreeInts(), "container-overflow"); | |
| } | |
| #endif // ADDRESS_SANITIZER | |
| } // namespace |