123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835 |
- // 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
- //
- // https://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 "ceres/internal/fixed_array.h"
- #include <cstdio>
- #include <cstring>
- #include <list>
- #include <memory>
- #include <numeric>
- #include <scoped_allocator>
- #include <stdexcept>
- #include <string>
- #include <vector>
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- using ::testing::ElementsAreArray;
- namespace {
- // CERES_INTERNAL_ARRAYSIZE()
- //
- // Returns the number of elements in an array as a compile-time constant, which
- // can be used in defining new arrays. If you use this macro on a pointer by
- // mistake, you will get a compile-time error.
- #define CERES_INTERNAL_ARRAYSIZE(array) (sizeof(ArraySizeHelper(array)))
- // Note: this internal template function declaration is used by
- // CERES_INTERNAL_ARRAYSIZE. The function doesn't need a definition, as we only
- // use its type.
- template <typename T, size_t N>
- auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
- // Helper routine to determine if a ceres::internal::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) { 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_{0};
- };
- 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) {
- ceres::internal::FixedArray<int, 10> on_stack(5);
- std::iota(on_stack.begin(), on_stack.end(), 0);
- ceres::internal::FixedArray<int, 10> stack_copy = on_stack;
- EXPECT_THAT(stack_copy, ElementsAreArray(on_stack));
- EXPECT_TRUE(IsOnStack(stack_copy));
- ceres::internal::FixedArray<int, 10> allocated(15);
- std::iota(allocated.begin(), allocated.end(), 0);
- ceres::internal::FixedArray<int, 10> alloced_copy = allocated;
- EXPECT_THAT(alloced_copy, ElementsAreArray(allocated));
- EXPECT_FALSE(IsOnStack(alloced_copy));
- }
- TEST(FixedArrayTest, MoveCtor) {
- ceres::internal::FixedArray<std::unique_ptr<int>, 10> on_stack(5);
- for (int i = 0; i < 5; ++i) {
- on_stack[i] = std::make_unique<int>(i);
- }
- ceres::internal::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());
- ceres::internal::FixedArray<std::unique_ptr<int>, 10> allocated(15);
- for (int i = 0; i < 15; ++i) {
- allocated[i] = std::make_unique<int>(i);
- }
- ceres::internal::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
- ceres::internal::FixedArray<int> array(4);
- EXPECT_TRUE(IsOnStack(array));
- }
- {
- // Large arrays should be on the heap
- ceres::internal::FixedArray<int> array(1048576);
- EXPECT_FALSE(IsOnStack(array));
- }
- {
- // Arrays of <= default size should be on the stack
- ceres::internal::FixedArray<int, 100> array(100);
- EXPECT_TRUE(IsOnStack(array));
- }
- {
- // Arrays of > default size should be on the heap
- ceres::internal::FixedArray<int, 100> array(101);
- EXPECT_FALSE(IsOnStack(array));
- }
- {
- // Arrays with different size elements should use approximately
- // same amount of stack space
- ceres::internal::FixedArray<int> array1(0);
- ceres::internal::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.
- ceres::internal::FixedArray<std::vector<int>> array(2);
- EXPECT_EQ(0, array[0].size());
- EXPECT_EQ(0, array[1].size());
- }
- {
- // Regardless of ceres::internal::FixedArray implementation, check that a
- // type with a low alignment requirement and a non power-of-two size is
- // initialized correctly.
- ThreeInts::counter = 1;
- ceres::internal::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(FixedArrayRelationalsTest, EqualArrays) {
- for (int i = 0; i < 10; ++i) {
- ceres::internal::FixedArray<int, 5> a1(i);
- std::iota(a1.begin(), a1.end(), 0);
- ceres::internal::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) {
- ceres::internal::FixedArray<int, 5> a1(i);
- std::iota(a1.begin(), a1.end(), 0);
- ceres::internal::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;
- {
- ceres::internal::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 = std::unique_ptr<
- ceres::internal::FixedArray<InnerArray, inline_elements>>(
- new ceres::internal::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};
- ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput) - 1> const
- fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
- ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
- for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
- ASSERT_EQ(kInput[i], fixed[i]);
- }
- }
- TEST(IteratorConstructorTest, Inline) {
- int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
- ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput)> const
- fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
- ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
- for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
- ASSERT_EQ(kInput[i], fixed[i]);
- }
- }
- TEST(IteratorConstructorTest, NonPod) {
- char const* kInput[] = {
- "red", "orange", "yellow", "green", "blue", "indigo", "violet"};
- ceres::internal::FixedArray<std::string> const fixed(
- kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
- ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
- for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
- ASSERT_EQ(kInput[i], fixed[i]);
- }
- }
- TEST(IteratorConstructorTest, FromEmptyVector) {
- std::vector<int> const empty;
- ceres::internal::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 + CERES_INTERNAL_ARRAYSIZE(kInput));
- ceres::internal::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 + CERES_INTERNAL_ARRAYSIZE(kInput));
- ceres::internal::FixedArray<int> const fixed(items.begin(), items.end());
- EXPECT_THAT(fixed, testing::ElementsAreArray(kInput));
- }
- TEST(InitListConstructorTest, InitListConstruction) {
- ceres::internal::FixedArray<int> fixed = {1, 2, 3};
- EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3}));
- }
- TEST(FillConstructorTest, NonEmptyArrays) {
- ceres::internal::FixedArray<int> stack_array(4, 1);
- EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
- ceres::internal::FixedArray<int, 0> heap_array(4, 1);
- EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
- }
- TEST(FillConstructorTest, EmptyArray) {
- ceres::internal::FixedArray<int> empty_fill(0, 1);
- ceres::internal::FixedArray<int> empty_size(0);
- EXPECT_EQ(empty_fill, empty_size);
- }
- TEST(FillConstructorTest, NotTriviallyCopyable) {
- std::string str = "abcd";
- ceres::internal::FixedArray<std::string> strings = {str, str, str, str};
- ceres::internal::FixedArray<std::string> array(4, str);
- EXPECT_EQ(array, strings);
- }
- TEST(FillConstructorTest, Disambiguation) {
- ceres::internal::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) {
- ceres::internal::FixedArray<char, 32> buf(32);
- snprintf(buf.data(), 32, "foo");
- }
- TEST(FixedArrayTest, TooBigInlinedSpace) {
- struct TooBig {
- char c[1 << 20];
- }; // too big for even one on the stack
- // Simulate the data members of ceres::internal::FixedArray, a pointer and a
- // size_t.
- struct Data {
- std::tuple<size_t, std::allocator<double>> size_alloc_;
- TooBig* p;
- };
- // Make sure TooBig objects are not inlined for 0 or default size.
- static_assert(
- sizeof(ceres::internal::FixedArray<TooBig, 0>) == sizeof(Data),
- "0-sized ceres::internal::FixedArray should have same size as Data.");
- static_assert(
- alignof(ceres::internal::FixedArray<TooBig, 0>) == alignof(Data),
- "0-sized ceres::internal::FixedArray should have same alignment as "
- "Data.");
- static_assert(sizeof(ceres::internal::FixedArray<TooBig>) == sizeof(Data),
- "default-sized ceres::internal::FixedArray should have same "
- "size as Data");
- static_assert(alignof(ceres::internal::FixedArray<TooBig>) == alignof(Data),
- "default-sized ceres::internal::FixedArray should have same "
- "alignment as Data.");
- }
- // PickyDelete EXPECTs its class-scope deallocation funcs are unused.
- struct 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) {
- ceres::internal::FixedArray<PickyDelete, 0> a(5);
- }
- TEST(FixedArrayTest, Data) {
- static const int kInput[] = {2, 3, 5, 7, 11, 13, 17};
- ceres::internal::FixedArray<int> fa(std::begin(kInput), std::end(kInput));
- EXPECT_EQ(fa.data(), &*fa.begin());
- EXPECT_EQ(fa.data(), &fa[0]);
- const ceres::internal::FixedArray<int>& cfa = fa;
- EXPECT_EQ(cfa.data(), &*cfa.begin());
- EXPECT_EQ(cfa.data(), &cfa[0]);
- }
- TEST(FixedArrayTest, Empty) {
- ceres::internal::FixedArray<int> empty(0);
- ceres::internal::FixedArray<int> inline_filled(1);
- ceres::internal::FixedArray<int, 0> heap_filled(1);
- EXPECT_TRUE(empty.empty());
- EXPECT_FALSE(inline_filled.empty());
- EXPECT_FALSE(heap_filled.empty());
- }
- TEST(FixedArrayTest, FrontAndBack) {
- ceres::internal::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3};
- EXPECT_EQ(inlined.front(), 1);
- EXPECT_EQ(inlined.back(), 3);
- ceres::internal::FixedArray<int, 0> allocated = {1, 2, 3};
- EXPECT_EQ(allocated.front(), 1);
- EXPECT_EQ(allocated.back(), 3);
- ceres::internal::FixedArray<int> one_element = {1};
- EXPECT_EQ(one_element.front(), one_element.back());
- }
- TEST(FixedArrayTest, ReverseIteratorInlined) {
- ceres::internal::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4};
- int counter = 5;
- for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
- iter != a.rend();
- ++iter) {
- counter--;
- EXPECT_EQ(counter, *iter);
- }
- EXPECT_EQ(counter, 0);
- counter = 5;
- for (ceres::internal::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) {
- ceres::internal::FixedArray<int, 0> a = {0, 1, 2, 3, 4};
- int counter = 5;
- for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
- iter != a.rend();
- ++iter) {
- counter--;
- EXPECT_EQ(counter, *iter);
- }
- EXPECT_EQ(counter, 0);
- counter = 5;
- for (ceres::internal::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) {
- ceres::internal::FixedArray<int, 5 * sizeof(int)> inlined(5);
- int fill_val = 42;
- inlined.fill(fill_val);
- for (int i : inlined) EXPECT_EQ(i, fill_val);
- ceres::internal::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.
- ceres::internal::FixedArray<int> empty(0);
- empty.fill(fill_val);
- }
- // TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x
- #ifndef __GNUC__
- TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
- using T = char;
- constexpr auto capacity = 10;
- using FixedArrType = ceres::internal::FixedArray<T, capacity>;
- using FixedArrBuffType =
- typename std::aligned_storage<sizeof(FixedArrType),
- alignof(FixedArrType)>::type;
- constexpr auto scrubbed_bits = 0x95;
- constexpr auto length = capacity / 2;
- FixedArrBuffType buff;
- std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType));
- FixedArrType* arr =
- ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
- EXPECT_THAT(*arr, testing::Each(scrubbed_bits));
- arr->~FixedArrType();
- }
- #endif // __GNUC__
- // This is a stateful allocator, but the state lives outside of the
- // allocator (in whatever test is using the allocator). This is odd
- // but helps in tests where the allocator is propagated into nested
- // containers - that chain of allocators uses the same state and is
- // thus easier to query for aggregate allocation information.
- template <typename T>
- class CountingAllocator : public std::allocator<T> {
- public:
- using Alloc = std::allocator<T>;
- using size_type = typename Alloc::size_type;
- CountingAllocator() = default;
- explicit CountingAllocator(int64_t* b) : bytes_used_(b) {}
- CountingAllocator(int64_t* b, int64_t* a)
- : bytes_used_(b), instance_count_(a) {}
- template <typename U>
- explicit CountingAllocator(const CountingAllocator<U>& x)
- : Alloc(x),
- bytes_used_(x.bytes_used_),
- instance_count_(x.instance_count_) {}
- T* allocate(size_type n) {
- assert(bytes_used_ != nullptr);
- *bytes_used_ += n * sizeof(T);
- return Alloc::allocate(n);
- }
- void deallocate(T* p, size_type n) {
- Alloc::deallocate(p, n);
- assert(bytes_used_ != nullptr);
- *bytes_used_ -= n * sizeof(T);
- }
- int64_t* bytes_used_{nullptr};
- int64_t* instance_count_{nullptr};
- };
- TEST(AllocatorSupportTest, CountInlineAllocations) {
- constexpr size_t inlined_size = 4;
- using Alloc = CountingAllocator<int>;
- using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
- int64_t allocated = 0;
- int64_t active_instances = 0;
- {
- const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
- Alloc alloc(&allocated, &active_instances);
- AllocFxdArr arr(ia, ia + inlined_size, alloc);
- static_cast<void>(arr);
- }
- EXPECT_EQ(allocated, 0);
- EXPECT_EQ(active_instances, 0);
- }
- TEST(AllocatorSupportTest, CountOutoflineAllocations) {
- constexpr size_t inlined_size = 4;
- using Alloc = CountingAllocator<int>;
- using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
- int64_t allocated = 0;
- int64_t active_instances = 0;
- {
- const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
- Alloc alloc(&allocated, &active_instances);
- AllocFxdArr arr(ia, ia + CERES_INTERNAL_ARRAYSIZE(ia), alloc);
- EXPECT_EQ(allocated, arr.size() * sizeof(int));
- static_cast<void>(arr);
- }
- EXPECT_EQ(active_instances, 0);
- }
- TEST(AllocatorSupportTest, CountCopyInlineAllocations) {
- constexpr size_t inlined_size = 4;
- using Alloc = CountingAllocator<int>;
- using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
- int64_t allocated1 = 0;
- int64_t allocated2 = 0;
- int64_t active_instances = 0;
- Alloc alloc(&allocated1, &active_instances);
- Alloc alloc2(&allocated2, &active_instances);
- {
- int initial_value = 1;
- AllocFxdArr arr1(inlined_size / 2, initial_value, alloc);
- EXPECT_EQ(allocated1, 0);
- AllocFxdArr arr2(arr1, alloc2);
- EXPECT_EQ(allocated2, 0);
- static_cast<void>(arr1);
- static_cast<void>(arr2);
- }
- EXPECT_EQ(active_instances, 0);
- }
- TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) {
- constexpr size_t inlined_size = 4;
- using Alloc = CountingAllocator<int>;
- using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
- int64_t allocated1 = 0;
- int64_t allocated2 = 0;
- int64_t active_instances = 0;
- Alloc alloc(&allocated1, &active_instances);
- Alloc alloc2(&allocated2, &active_instances);
- {
- int initial_value = 1;
- AllocFxdArr arr1(inlined_size * 2, initial_value, alloc);
- EXPECT_EQ(allocated1, arr1.size() * sizeof(int));
- AllocFxdArr arr2(arr1, alloc2);
- EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int));
- static_cast<void>(arr1);
- static_cast<void>(arr2);
- }
- EXPECT_EQ(active_instances, 0);
- }
- TEST(AllocatorSupportTest, SizeValAllocConstructor) {
- using testing::AllOf;
- using testing::Each;
- using testing::SizeIs;
- constexpr size_t inlined_size = 4;
- using Alloc = CountingAllocator<int>;
- using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
- {
- auto len = inlined_size / 2;
- auto val = 0;
- int64_t allocated = 0;
- AllocFxdArr arr(len, val, Alloc(&allocated));
- EXPECT_EQ(allocated, 0);
- EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
- }
- {
- auto len = inlined_size * 2;
- auto val = 0;
- int64_t allocated = 0;
- AllocFxdArr arr(len, val, Alloc(&allocated));
- EXPECT_EQ(allocated, len * sizeof(int));
- EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
- }
- }
- struct EigenStruct {
- Eigen::Vector4d data;
- };
- static_assert(
- std::is_same<ceres::internal::FixedArrayDefaultAllocator<double>,
- std::allocator<double>>::value,
- "Double is a trivial type, so std::allocator should be used here.");
- static_assert(
- std::is_same<ceres::internal::FixedArrayDefaultAllocator<double*>,
- std::allocator<double*>>::value,
- "A pointer is a trivial type, so std::allocator should be used here.");
- static_assert(
- std::is_same<ceres::internal::FixedArrayDefaultAllocator<Eigen::Matrix4d>,
- Eigen::aligned_allocator<Eigen::Matrix4d>>::value,
- "An Eigen::Matrix4d needs the Eigen::aligned_allocator for proper "
- "alignment.");
- static_assert(
- std::is_same<ceres::internal::FixedArrayDefaultAllocator<EigenStruct>,
- Eigen::aligned_allocator<EigenStruct>>::value,
- "A struct containing fixed size Eigen types needs Eigen::aligned_allocator "
- "for proper alignment.");
- } // namespace
|