blob: d418786a5d2d11c7b0fe083043623cadba351ce1 [file] [log] [blame]
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08001// Copyright 2017 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "ceres/internal/fixed_array.h"
16
17#include <stdio.h>
18
19#include <cstring>
20#include <list>
21#include <memory>
22#include <numeric>
23#include <scoped_allocator>
24#include <stdexcept>
25#include <string>
26#include <vector>
27
28#include "gmock/gmock.h"
29#include "gtest/gtest.h"
30
31using ::testing::ElementsAreArray;
32
33namespace {
34
35// CERES_INTERNAL_ARRAYSIZE()
36//
37// Returns the number of elements in an array as a compile-time constant, which
38// can be used in defining new arrays. If you use this macro on a pointer by
39// mistake, you will get a compile-time error.
40#define CERES_INTERNAL_ARRAYSIZE(array) (sizeof(ArraySizeHelper(array)))
41
42// Note: this internal template function declaration is used by
43// CERES_INTERNAL_ARRAYSIZE. The function doesn't need a definition, as we only
44// use its type.
45template <typename T, size_t N>
46auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
47
48// Helper routine to determine if a ceres::internal::FixedArray used stack
49// allocation.
50template <typename ArrayType>
51static bool IsOnStack(const ArrayType& a) {
52 return a.size() <= ArrayType::inline_elements;
53}
54
55class ConstructionTester {
56 public:
57 ConstructionTester() : self_ptr_(this), value_(0) { constructions++; }
58 ~ConstructionTester() {
59 assert(self_ptr_ == this);
60 self_ptr_ = nullptr;
61 destructions++;
62 }
63
64 // These are incremented as elements are constructed and destructed so we can
65 // be sure all elements are properly cleaned up.
66 static int constructions;
67 static int destructions;
68
69 void CheckConstructed() { assert(self_ptr_ == this); }
70
71 void set(int value) { value_ = value; }
72 int get() { return value_; }
73
74 private:
75 // self_ptr_ should always point to 'this' -- that's how we can be sure the
76 // constructor has been called.
77 ConstructionTester* self_ptr_;
78 int value_;
79};
80
81int ConstructionTester::constructions = 0;
82int ConstructionTester::destructions = 0;
83
84// ThreeInts will initialize its three ints to the value stored in
85// ThreeInts::counter. The constructor increments counter so that each object
86// in an array of ThreeInts will have different values.
87class ThreeInts {
88 public:
89 ThreeInts() {
90 x_ = counter;
91 y_ = counter;
92 z_ = counter;
93 ++counter;
94 }
95
96 static int counter;
97
98 int x_, y_, z_;
99};
100
101int ThreeInts::counter = 0;
102
103TEST(FixedArrayTest, CopyCtor) {
104 ceres::internal::FixedArray<int, 10> on_stack(5);
105 std::iota(on_stack.begin(), on_stack.end(), 0);
106 ceres::internal::FixedArray<int, 10> stack_copy = on_stack;
107 EXPECT_THAT(stack_copy, ElementsAreArray(on_stack));
108 EXPECT_TRUE(IsOnStack(stack_copy));
109
110 ceres::internal::FixedArray<int, 10> allocated(15);
111 std::iota(allocated.begin(), allocated.end(), 0);
112 ceres::internal::FixedArray<int, 10> alloced_copy = allocated;
113 EXPECT_THAT(alloced_copy, ElementsAreArray(allocated));
114 EXPECT_FALSE(IsOnStack(alloced_copy));
115}
116
117TEST(FixedArrayTest, MoveCtor) {
118 ceres::internal::FixedArray<std::unique_ptr<int>, 10> on_stack(5);
119 for (int i = 0; i < 5; ++i) {
120 on_stack[i] = std::unique_ptr<int>(new int(i));
121 }
122
123 ceres::internal::FixedArray<std::unique_ptr<int>, 10> stack_copy =
124 std::move(on_stack);
125 for (int i = 0; i < 5; ++i) EXPECT_EQ(*(stack_copy[i]), i);
126 EXPECT_EQ(stack_copy.size(), on_stack.size());
127
128 ceres::internal::FixedArray<std::unique_ptr<int>, 10> allocated(15);
129 for (int i = 0; i < 15; ++i) {
130 allocated[i] = std::unique_ptr<int>(new int(i));
131 }
132
133 ceres::internal::FixedArray<std::unique_ptr<int>, 10> alloced_copy =
134 std::move(allocated);
135 for (int i = 0; i < 15; ++i) EXPECT_EQ(*(alloced_copy[i]), i);
136 EXPECT_EQ(allocated.size(), alloced_copy.size());
137}
138
139TEST(FixedArrayTest, SmallObjects) {
140 // Small object arrays
141 {
142 // Short arrays should be on the stack
143 ceres::internal::FixedArray<int> array(4);
144 EXPECT_TRUE(IsOnStack(array));
145 }
146
147 {
148 // Large arrays should be on the heap
149 ceres::internal::FixedArray<int> array(1048576);
150 EXPECT_FALSE(IsOnStack(array));
151 }
152
153 {
154 // Arrays of <= default size should be on the stack
155 ceres::internal::FixedArray<int, 100> array(100);
156 EXPECT_TRUE(IsOnStack(array));
157 }
158
159 {
160 // Arrays of > default size should be on the heap
161 ceres::internal::FixedArray<int, 100> array(101);
162 EXPECT_FALSE(IsOnStack(array));
163 }
164
165 {
166 // Arrays with different size elements should use approximately
167 // same amount of stack space
168 ceres::internal::FixedArray<int> array1(0);
169 ceres::internal::FixedArray<char> array2(0);
170 EXPECT_LE(sizeof(array1), sizeof(array2) + 100);
171 EXPECT_LE(sizeof(array2), sizeof(array1) + 100);
172 }
173
174 {
175 // Ensure that vectors are properly constructed inside a fixed array.
176 ceres::internal::FixedArray<std::vector<int>> array(2);
177 EXPECT_EQ(0, array[0].size());
178 EXPECT_EQ(0, array[1].size());
179 }
180
181 {
182 // Regardless of ceres::internal::FixedArray implementation, check that a
183 // type with a low alignment requirement and a non power-of-two size is
184 // initialized correctly.
185 ThreeInts::counter = 1;
186 ceres::internal::FixedArray<ThreeInts> array(2);
187 EXPECT_EQ(1, array[0].x_);
188 EXPECT_EQ(1, array[0].y_);
189 EXPECT_EQ(1, array[0].z_);
190 EXPECT_EQ(2, array[1].x_);
191 EXPECT_EQ(2, array[1].y_);
192 EXPECT_EQ(2, array[1].z_);
193 }
194}
195
196TEST(FixedArrayRelationalsTest, EqualArrays) {
197 for (int i = 0; i < 10; ++i) {
198 ceres::internal::FixedArray<int, 5> a1(i);
199 std::iota(a1.begin(), a1.end(), 0);
200 ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end());
201
202 EXPECT_TRUE(a1 == a2);
203 EXPECT_FALSE(a1 != a2);
204 EXPECT_TRUE(a2 == a1);
205 EXPECT_FALSE(a2 != a1);
206 EXPECT_FALSE(a1 < a2);
207 EXPECT_FALSE(a1 > a2);
208 EXPECT_FALSE(a2 < a1);
209 EXPECT_FALSE(a2 > a1);
210 EXPECT_TRUE(a1 <= a2);
211 EXPECT_TRUE(a1 >= a2);
212 EXPECT_TRUE(a2 <= a1);
213 EXPECT_TRUE(a2 >= a1);
214 }
215}
216
217TEST(FixedArrayRelationalsTest, UnequalArrays) {
218 for (int i = 1; i < 10; ++i) {
219 ceres::internal::FixedArray<int, 5> a1(i);
220 std::iota(a1.begin(), a1.end(), 0);
221 ceres::internal::FixedArray<int, 5> a2(a1.begin(), a1.end());
222 --a2[i / 2];
223
224 EXPECT_FALSE(a1 == a2);
225 EXPECT_TRUE(a1 != a2);
226 EXPECT_FALSE(a2 == a1);
227 EXPECT_TRUE(a2 != a1);
228 EXPECT_FALSE(a1 < a2);
229 EXPECT_TRUE(a1 > a2);
230 EXPECT_TRUE(a2 < a1);
231 EXPECT_FALSE(a2 > a1);
232 EXPECT_FALSE(a1 <= a2);
233 EXPECT_TRUE(a1 >= a2);
234 EXPECT_TRUE(a2 <= a1);
235 EXPECT_FALSE(a2 >= a1);
236 }
237}
238
239template <int stack_elements>
240static void TestArray(int n) {
241 SCOPED_TRACE(n);
242 SCOPED_TRACE(stack_elements);
243 ConstructionTester::constructions = 0;
244 ConstructionTester::destructions = 0;
245 {
246 ceres::internal::FixedArray<ConstructionTester, stack_elements> array(n);
247
248 EXPECT_THAT(array.size(), n);
249 EXPECT_THAT(array.memsize(), sizeof(ConstructionTester) * n);
250 EXPECT_THAT(array.begin() + n, array.end());
251
252 // Check that all elements were constructed
253 for (int i = 0; i < n; i++) {
254 array[i].CheckConstructed();
255 }
256 // Check that no other elements were constructed
257 EXPECT_THAT(ConstructionTester::constructions, n);
258
259 // Test operator[]
260 for (int i = 0; i < n; i++) {
261 array[i].set(i);
262 }
263 for (int i = 0; i < n; i++) {
264 EXPECT_THAT(array[i].get(), i);
265 EXPECT_THAT(array.data()[i].get(), i);
266 }
267
268 // Test data()
269 for (int i = 0; i < n; i++) {
270 array.data()[i].set(i + 1);
271 }
272 for (int i = 0; i < n; i++) {
273 EXPECT_THAT(array[i].get(), i + 1);
274 EXPECT_THAT(array.data()[i].get(), i + 1);
275 }
276 } // Close scope containing 'array'.
277
278 // Check that all constructed elements were destructed.
279 EXPECT_EQ(ConstructionTester::constructions,
280 ConstructionTester::destructions);
281}
282
283template <int elements_per_inner_array, int inline_elements>
284static void TestArrayOfArrays(int n) {
285 SCOPED_TRACE(n);
286 SCOPED_TRACE(inline_elements);
287 SCOPED_TRACE(elements_per_inner_array);
288 ConstructionTester::constructions = 0;
289 ConstructionTester::destructions = 0;
290 {
291 using InnerArray = ConstructionTester[elements_per_inner_array];
292 // Heap-allocate the FixedArray to avoid blowing the stack frame.
293 auto array_ptr = std::unique_ptr<
294 ceres::internal::FixedArray<InnerArray, inline_elements>>(
295 new ceres::internal::FixedArray<InnerArray, inline_elements>(n));
296 auto& array = *array_ptr;
297
298 ASSERT_EQ(array.size(), n);
299 ASSERT_EQ(array.memsize(),
300 sizeof(ConstructionTester) * elements_per_inner_array * n);
301 ASSERT_EQ(array.begin() + n, array.end());
302
303 // Check that all elements were constructed
304 for (int i = 0; i < n; i++) {
305 for (int j = 0; j < elements_per_inner_array; j++) {
306 (array[i])[j].CheckConstructed();
307 }
308 }
309 // Check that no other elements were constructed
310 ASSERT_EQ(ConstructionTester::constructions, n * elements_per_inner_array);
311
312 // Test operator[]
313 for (int i = 0; i < n; i++) {
314 for (int j = 0; j < elements_per_inner_array; j++) {
315 (array[i])[j].set(i * elements_per_inner_array + j);
316 }
317 }
318 for (int i = 0; i < n; i++) {
319 for (int j = 0; j < elements_per_inner_array; j++) {
320 ASSERT_EQ((array[i])[j].get(), i * elements_per_inner_array + j);
321 ASSERT_EQ((array.data()[i])[j].get(), i * elements_per_inner_array + j);
322 }
323 }
324
325 // Test data()
326 for (int i = 0; i < n; i++) {
327 for (int j = 0; j < elements_per_inner_array; j++) {
328 (array.data()[i])[j].set((i + 1) * elements_per_inner_array + j);
329 }
330 }
331 for (int i = 0; i < n; i++) {
332 for (int j = 0; j < elements_per_inner_array; j++) {
333 ASSERT_EQ((array[i])[j].get(), (i + 1) * elements_per_inner_array + j);
334 ASSERT_EQ((array.data()[i])[j].get(),
335 (i + 1) * elements_per_inner_array + j);
336 }
337 }
338 } // Close scope containing 'array'.
339
340 // Check that all constructed elements were destructed.
341 EXPECT_EQ(ConstructionTester::constructions,
342 ConstructionTester::destructions);
343}
344
345TEST(IteratorConstructorTest, NonInline) {
346 int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
347 ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput) - 1> const
348 fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
349 ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
350 for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
351 ASSERT_EQ(kInput[i], fixed[i]);
352 }
353}
354
355TEST(IteratorConstructorTest, Inline) {
356 int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
357 ceres::internal::FixedArray<int, CERES_INTERNAL_ARRAYSIZE(kInput)> const
358 fixed(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
359 ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
360 for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
361 ASSERT_EQ(kInput[i], fixed[i]);
362 }
363}
364
365TEST(IteratorConstructorTest, NonPod) {
366 char const* kInput[] = {
367 "red", "orange", "yellow", "green", "blue", "indigo", "violet"};
368 ceres::internal::FixedArray<std::string> const fixed(
369 kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
370 ASSERT_EQ(CERES_INTERNAL_ARRAYSIZE(kInput), fixed.size());
371 for (size_t i = 0; i < CERES_INTERNAL_ARRAYSIZE(kInput); ++i) {
372 ASSERT_EQ(kInput[i], fixed[i]);
373 }
374}
375
376TEST(IteratorConstructorTest, FromEmptyVector) {
377 std::vector<int> const empty;
378 ceres::internal::FixedArray<int> const fixed(empty.begin(), empty.end());
379 EXPECT_EQ(0, fixed.size());
380 EXPECT_EQ(empty.size(), fixed.size());
381}
382
383TEST(IteratorConstructorTest, FromNonEmptyVector) {
384 int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
385 std::vector<int> const items(kInput,
386 kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
387 ceres::internal::FixedArray<int> const fixed(items.begin(), items.end());
388 ASSERT_EQ(items.size(), fixed.size());
389 for (size_t i = 0; i < items.size(); ++i) {
390 ASSERT_EQ(items[i], fixed[i]);
391 }
392}
393
394TEST(IteratorConstructorTest, FromBidirectionalIteratorRange) {
395 int const kInput[] = {2, 3, 5, 7, 11, 13, 17};
396 std::list<int> const items(kInput, kInput + CERES_INTERNAL_ARRAYSIZE(kInput));
397 ceres::internal::FixedArray<int> const fixed(items.begin(), items.end());
398 EXPECT_THAT(fixed, testing::ElementsAreArray(kInput));
399}
400
401TEST(InitListConstructorTest, InitListConstruction) {
402 ceres::internal::FixedArray<int> fixed = {1, 2, 3};
403 EXPECT_THAT(fixed, testing::ElementsAreArray({1, 2, 3}));
404}
405
406TEST(FillConstructorTest, NonEmptyArrays) {
407 ceres::internal::FixedArray<int> stack_array(4, 1);
408 EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
409
410 ceres::internal::FixedArray<int, 0> heap_array(4, 1);
411 EXPECT_THAT(stack_array, testing::ElementsAreArray({1, 1, 1, 1}));
412}
413
414TEST(FillConstructorTest, EmptyArray) {
415 ceres::internal::FixedArray<int> empty_fill(0, 1);
416 ceres::internal::FixedArray<int> empty_size(0);
417 EXPECT_EQ(empty_fill, empty_size);
418}
419
420TEST(FillConstructorTest, NotTriviallyCopyable) {
421 std::string str = "abcd";
422 ceres::internal::FixedArray<std::string> strings = {str, str, str, str};
423
424 ceres::internal::FixedArray<std::string> array(4, str);
425 EXPECT_EQ(array, strings);
426}
427
428TEST(FillConstructorTest, Disambiguation) {
429 ceres::internal::FixedArray<size_t> a(1, 2);
430 EXPECT_THAT(a, testing::ElementsAre(2));
431}
432
433TEST(FixedArrayTest, ManySizedArrays) {
434 std::vector<int> sizes;
435 for (int i = 1; i < 100; i++) sizes.push_back(i);
436 for (int i = 100; i <= 1000; i += 100) sizes.push_back(i);
437 for (int n : sizes) {
438 TestArray<0>(n);
439 TestArray<1>(n);
440 TestArray<64>(n);
441 TestArray<1000>(n);
442 }
443}
444
445TEST(FixedArrayTest, ManySizedArraysOfArraysOf1) {
446 for (int n = 1; n < 1000; n++) {
447 ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 0>(n)));
448 ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1>(n)));
449 ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 64>(n)));
450 ASSERT_NO_FATAL_FAILURE((TestArrayOfArrays<1, 1000>(n)));
451 }
452}
453
454TEST(FixedArrayTest, ManySizedArraysOfArraysOf2) {
455 for (int n = 1; n < 1000; n++) {
456 TestArrayOfArrays<2, 0>(n);
457 TestArrayOfArrays<2, 1>(n);
458 TestArrayOfArrays<2, 64>(n);
459 TestArrayOfArrays<2, 1000>(n);
460 }
461}
462
463// If value_type is put inside of a struct container,
464// we might evoke this error in a hardened build unless data() is carefully
465// written, so check on that.
466// error: call to int __builtin___sprintf_chk(etc...)
467// will always overflow destination buffer [-Werror]
468TEST(FixedArrayTest, AvoidParanoidDiagnostics) {
469 ceres::internal::FixedArray<char, 32> buf(32);
470 sprintf(buf.data(), "foo"); // NOLINT(runtime/printf)
471}
472
473TEST(FixedArrayTest, TooBigInlinedSpace) {
474 struct TooBig {
475 char c[1 << 20];
476 }; // too big for even one on the stack
477
478 // Simulate the data members of ceres::internal::FixedArray, a pointer and a
479 // size_t.
480 struct Data {
481 std::tuple<size_t, std::allocator<double>> size_alloc_;
482 TooBig* p;
483 };
484
485 // Make sure TooBig objects are not inlined for 0 or default size.
486 static_assert(
487 sizeof(ceres::internal::FixedArray<TooBig, 0>) == sizeof(Data),
488 "0-sized ceres::internal::FixedArray should have same size as Data.");
489 static_assert(
490 alignof(ceres::internal::FixedArray<TooBig, 0>) == alignof(Data),
491 "0-sized ceres::internal::FixedArray should have same alignment as "
492 "Data.");
493 static_assert(sizeof(ceres::internal::FixedArray<TooBig>) == sizeof(Data),
494 "default-sized ceres::internal::FixedArray should have same "
495 "size as Data");
496 static_assert(alignof(ceres::internal::FixedArray<TooBig>) == alignof(Data),
497 "default-sized ceres::internal::FixedArray should have same "
498 "alignment as Data.");
499}
500
501// PickyDelete EXPECTs its class-scope deallocation funcs are unused.
502struct PickyDelete {
503 PickyDelete() {}
504 ~PickyDelete() {}
505 void operator delete(void* p) {
506 EXPECT_TRUE(false) << __FUNCTION__;
507 ::operator delete(p);
508 }
509 void operator delete[](void* p) {
510 EXPECT_TRUE(false) << __FUNCTION__;
511 ::operator delete[](p);
512 }
513};
514
515TEST(FixedArrayTest, UsesGlobalAlloc) {
516 ceres::internal::FixedArray<PickyDelete, 0> a(5);
517}
518
519TEST(FixedArrayTest, Data) {
520 static const int kInput[] = {2, 3, 5, 7, 11, 13, 17};
521 ceres::internal::FixedArray<int> fa(std::begin(kInput), std::end(kInput));
522 EXPECT_EQ(fa.data(), &*fa.begin());
523 EXPECT_EQ(fa.data(), &fa[0]);
524
525 const ceres::internal::FixedArray<int>& cfa = fa;
526 EXPECT_EQ(cfa.data(), &*cfa.begin());
527 EXPECT_EQ(cfa.data(), &cfa[0]);
528}
529
530TEST(FixedArrayTest, Empty) {
531 ceres::internal::FixedArray<int> empty(0);
532 ceres::internal::FixedArray<int> inline_filled(1);
533 ceres::internal::FixedArray<int, 0> heap_filled(1);
534 EXPECT_TRUE(empty.empty());
535 EXPECT_FALSE(inline_filled.empty());
536 EXPECT_FALSE(heap_filled.empty());
537}
538
539TEST(FixedArrayTest, FrontAndBack) {
540 ceres::internal::FixedArray<int, 3 * sizeof(int)> inlined = {1, 2, 3};
541 EXPECT_EQ(inlined.front(), 1);
542 EXPECT_EQ(inlined.back(), 3);
543
544 ceres::internal::FixedArray<int, 0> allocated = {1, 2, 3};
545 EXPECT_EQ(allocated.front(), 1);
546 EXPECT_EQ(allocated.back(), 3);
547
548 ceres::internal::FixedArray<int> one_element = {1};
549 EXPECT_EQ(one_element.front(), one_element.back());
550}
551
552TEST(FixedArrayTest, ReverseIteratorInlined) {
553 ceres::internal::FixedArray<int, 5 * sizeof(int)> a = {0, 1, 2, 3, 4};
554
555 int counter = 5;
556 for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
557 iter != a.rend();
558 ++iter) {
559 counter--;
560 EXPECT_EQ(counter, *iter);
561 }
562 EXPECT_EQ(counter, 0);
563
564 counter = 5;
565 for (ceres::internal::FixedArray<int>::const_reverse_iterator iter =
566 a.rbegin();
567 iter != a.rend();
568 ++iter) {
569 counter--;
570 EXPECT_EQ(counter, *iter);
571 }
572 EXPECT_EQ(counter, 0);
573
574 counter = 5;
575 for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
576 counter--;
577 EXPECT_EQ(counter, *iter);
578 }
579 EXPECT_EQ(counter, 0);
580}
581
582TEST(FixedArrayTest, ReverseIteratorAllocated) {
583 ceres::internal::FixedArray<int, 0> a = {0, 1, 2, 3, 4};
584
585 int counter = 5;
586 for (ceres::internal::FixedArray<int>::reverse_iterator iter = a.rbegin();
587 iter != a.rend();
588 ++iter) {
589 counter--;
590 EXPECT_EQ(counter, *iter);
591 }
592 EXPECT_EQ(counter, 0);
593
594 counter = 5;
595 for (ceres::internal::FixedArray<int>::const_reverse_iterator iter =
596 a.rbegin();
597 iter != a.rend();
598 ++iter) {
599 counter--;
600 EXPECT_EQ(counter, *iter);
601 }
602 EXPECT_EQ(counter, 0);
603
604 counter = 5;
605 for (auto iter = a.crbegin(); iter != a.crend(); ++iter) {
606 counter--;
607 EXPECT_EQ(counter, *iter);
608 }
609 EXPECT_EQ(counter, 0);
610}
611
612TEST(FixedArrayTest, Fill) {
613 ceres::internal::FixedArray<int, 5 * sizeof(int)> inlined(5);
614 int fill_val = 42;
615 inlined.fill(fill_val);
616 for (int i : inlined) EXPECT_EQ(i, fill_val);
617
618 ceres::internal::FixedArray<int, 0> allocated(5);
619 allocated.fill(fill_val);
620 for (int i : allocated) EXPECT_EQ(i, fill_val);
621
622 // It doesn't do anything, just make sure this compiles.
623 ceres::internal::FixedArray<int> empty(0);
624 empty.fill(fill_val);
625}
626
627// TODO(johnsoncj): Investigate InlinedStorage default initialization in GCC 4.x
628#ifndef __GNUC__
629TEST(FixedArrayTest, DefaultCtorDoesNotValueInit) {
630 using T = char;
631 constexpr auto capacity = 10;
632 using FixedArrType = ceres::internal::FixedArray<T, capacity>;
633 using FixedArrBuffType =
634 typename std::aligned_storage<sizeof(FixedArrType),
635 alignof(FixedArrType)>::type;
636 constexpr auto scrubbed_bits = 0x95;
637 constexpr auto length = capacity / 2;
638
639 FixedArrBuffType buff;
640 std::memset(std::addressof(buff), scrubbed_bits, sizeof(FixedArrBuffType));
641
642 FixedArrType* arr =
643 ::new (static_cast<void*>(std::addressof(buff))) FixedArrType(length);
644 EXPECT_THAT(*arr, testing::Each(scrubbed_bits));
645 arr->~FixedArrType();
646}
647#endif // __GNUC__
648
649// This is a stateful allocator, but the state lives outside of the
650// allocator (in whatever test is using the allocator). This is odd
651// but helps in tests where the allocator is propagated into nested
652// containers - that chain of allocators uses the same state and is
653// thus easier to query for aggregate allocation information.
654template <typename T>
655class CountingAllocator : public std::allocator<T> {
656 public:
657 using Alloc = std::allocator<T>;
658 using pointer = typename Alloc::pointer;
659 using size_type = typename Alloc::size_type;
660
661 CountingAllocator() : bytes_used_(nullptr), instance_count_(nullptr) {}
662 explicit CountingAllocator(int64_t* b)
663 : bytes_used_(b), instance_count_(nullptr) {}
664 CountingAllocator(int64_t* b, int64_t* a)
665 : bytes_used_(b), instance_count_(a) {}
666
667 template <typename U>
668 explicit CountingAllocator(const CountingAllocator<U>& x)
669 : Alloc(x),
670 bytes_used_(x.bytes_used_),
671 instance_count_(x.instance_count_) {}
672
673 pointer allocate(size_type n, const void* const hint = nullptr) {
674 assert(bytes_used_ != nullptr);
675 *bytes_used_ += n * sizeof(T);
676 return Alloc::allocate(n, hint);
677 }
678
679 void deallocate(pointer p, size_type n) {
680 Alloc::deallocate(p, n);
681 assert(bytes_used_ != nullptr);
682 *bytes_used_ -= n * sizeof(T);
683 }
684
685 template <typename... Args>
686 void construct(pointer p, Args&&... args) {
687 Alloc::construct(p, std::forward<Args>(args)...);
688 if (instance_count_) {
689 *instance_count_ += 1;
690 }
691 }
692
693 void destroy(pointer p) {
694 Alloc::destroy(p);
695 if (instance_count_) {
696 *instance_count_ -= 1;
697 }
698 }
699
700 template <typename U>
701 class rebind {
702 public:
703 using other = CountingAllocator<U>;
704 };
705
706 int64_t* bytes_used_;
707 int64_t* instance_count_;
708};
709
710TEST(AllocatorSupportTest, CountInlineAllocations) {
711 constexpr size_t inlined_size = 4;
712 using Alloc = CountingAllocator<int>;
713 using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
714
715 int64_t allocated = 0;
716 int64_t active_instances = 0;
717
718 {
719 const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
720
721 Alloc alloc(&allocated, &active_instances);
722
723 AllocFxdArr arr(ia, ia + inlined_size, alloc);
724 static_cast<void>(arr);
725 }
726
727 EXPECT_EQ(allocated, 0);
728 EXPECT_EQ(active_instances, 0);
729}
730
731TEST(AllocatorSupportTest, CountOutoflineAllocations) {
732 constexpr size_t inlined_size = 4;
733 using Alloc = CountingAllocator<int>;
734 using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
735
736 int64_t allocated = 0;
737 int64_t active_instances = 0;
738
739 {
740 const int ia[] = {0, 1, 2, 3, 4, 5, 6, 7};
741 Alloc alloc(&allocated, &active_instances);
742
743 AllocFxdArr arr(ia, ia + CERES_INTERNAL_ARRAYSIZE(ia), alloc);
744
745 EXPECT_EQ(allocated, arr.size() * sizeof(int));
746 static_cast<void>(arr);
747 }
748
749 EXPECT_EQ(active_instances, 0);
750}
751
752TEST(AllocatorSupportTest, CountCopyInlineAllocations) {
753 constexpr size_t inlined_size = 4;
754 using Alloc = CountingAllocator<int>;
755 using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
756
757 int64_t allocated1 = 0;
758 int64_t allocated2 = 0;
759 int64_t active_instances = 0;
760 Alloc alloc(&allocated1, &active_instances);
761 Alloc alloc2(&allocated2, &active_instances);
762
763 {
764 int initial_value = 1;
765
766 AllocFxdArr arr1(inlined_size / 2, initial_value, alloc);
767
768 EXPECT_EQ(allocated1, 0);
769
770 AllocFxdArr arr2(arr1, alloc2);
771
772 EXPECT_EQ(allocated2, 0);
773 static_cast<void>(arr1);
774 static_cast<void>(arr2);
775 }
776
777 EXPECT_EQ(active_instances, 0);
778}
779
780TEST(AllocatorSupportTest, CountCopyOutoflineAllocations) {
781 constexpr size_t inlined_size = 4;
782 using Alloc = CountingAllocator<int>;
783 using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
784
785 int64_t allocated1 = 0;
786 int64_t allocated2 = 0;
787 int64_t active_instances = 0;
788 Alloc alloc(&allocated1, &active_instances);
789 Alloc alloc2(&allocated2, &active_instances);
790
791 {
792 int initial_value = 1;
793
794 AllocFxdArr arr1(inlined_size * 2, initial_value, alloc);
795
796 EXPECT_EQ(allocated1, arr1.size() * sizeof(int));
797
798 AllocFxdArr arr2(arr1, alloc2);
799
800 EXPECT_EQ(allocated2, inlined_size * 2 * sizeof(int));
801 static_cast<void>(arr1);
802 static_cast<void>(arr2);
803 }
804
805 EXPECT_EQ(active_instances, 0);
806}
807
808TEST(AllocatorSupportTest, SizeValAllocConstructor) {
809 using testing::AllOf;
810 using testing::Each;
811 using testing::SizeIs;
812
813 constexpr size_t inlined_size = 4;
814 using Alloc = CountingAllocator<int>;
815 using AllocFxdArr = ceres::internal::FixedArray<int, inlined_size, Alloc>;
816
817 {
818 auto len = inlined_size / 2;
819 auto val = 0;
820 int64_t allocated = 0;
821 AllocFxdArr arr(len, val, Alloc(&allocated));
822
823 EXPECT_EQ(allocated, 0);
824 EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
825 }
826
827 {
828 auto len = inlined_size * 2;
829 auto val = 0;
830 int64_t allocated = 0;
831 AllocFxdArr arr(len, val, Alloc(&allocated));
832
833 EXPECT_EQ(allocated, len * sizeof(int));
834 EXPECT_THAT(arr, AllOf(SizeIs(len), Each(0)));
835 }
836}
837
838struct EigenStruct {
839 Eigen::Vector4d data;
840};
841
842static_assert(
843 std::is_same<ceres::internal::FixedArrayDefaultAllocator<double>,
844 std::allocator<double>>::value,
845 "Double is a trivial type, so std::allocator should be used here.");
846static_assert(
847 std::is_same<ceres::internal::FixedArrayDefaultAllocator<double*>,
848 std::allocator<double*>>::value,
849 "A pointer is a trivial type, so std::allocator should be used here.");
850static_assert(
851 std::is_same<ceres::internal::FixedArrayDefaultAllocator<Eigen::Matrix4d>,
852 Eigen::aligned_allocator<Eigen::Matrix4d>>::value,
853 "An Eigen::Matrix4d needs the Eigen::aligned_allocator for proper "
854 "alignment.");
855static_assert(
856 std::is_same<ceres::internal::FixedArrayDefaultAllocator<EigenStruct>,
857 Eigen::aligned_allocator<EigenStruct>>::value,
858 "A struct containing fixed size Eigen types needs Eigen::aligned_allocator "
859 "for proper alignment.");
860
861} // namespace