1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/contiguous_container.h"
6 
7 #include "testing/gmock/include/gmock/gmock.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
10 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
11 
12 namespace blink {
13 namespace {
14 
15 struct Point2D {
Point2Dblink::__anon289e0c970111::Point2D16   Point2D() : Point2D(0, 0) {}
Point2Dblink::__anon289e0c970111::Point2D17   Point2D(int x, int y) : x(x), y(y) {}
18   int x, y;
19 };
20 
21 struct Point3D : public Point2D {
Point3Dblink::__anon289e0c970111::Point3D22   Point3D() : Point3D(0, 0, 0) {}
Point3Dblink::__anon289e0c970111::Point3D23   Point3D(int x, int y, int z) : Point2D(x, y), z(z) {}
24   int z;
25 };
26 
27 // Maximum size of a subclass of Point2D.
28 static const size_t kMaxPointSize = sizeof(Point3D);
29 
30 // Alignment for Point2D and its subclasses.
31 static const size_t kPointAlignment = sizeof(int);
32 
33 // How many elements to use for tests with "plenty" of elements.
34 static const unsigned kNumElements = 150;
35 
TEST(ContiguousContainerTest,SimpleStructs)36 TEST(ContiguousContainerTest, SimpleStructs) {
37   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
38   list.AllocateAndConstruct<Point2D>(1, 2);
39   list.AllocateAndConstruct<Point3D>(3, 4, 5);
40   list.AllocateAndConstruct<Point2D>(6, 7);
41 
42   ASSERT_EQ(3u, list.size());
43   EXPECT_EQ(1, list[0].x);
44   EXPECT_EQ(2, list[0].y);
45   EXPECT_EQ(3, list[1].x);
46   EXPECT_EQ(4, list[1].y);
47   EXPECT_EQ(5, static_cast<Point3D&>(list[1]).z);
48   EXPECT_EQ(6, list[2].x);
49   EXPECT_EQ(7, list[2].y);
50 }
51 
TEST(ContiguousContainerTest,AllocateLots)52 TEST(ContiguousContainerTest, AllocateLots) {
53   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
54   for (int i = 0; i < (int)kNumElements; i++) {
55     list.AllocateAndConstruct<Point2D>(i, i);
56     list.AllocateAndConstruct<Point2D>(i, i);
57     list.RemoveLast();
58   }
59   ASSERT_EQ(kNumElements, list.size());
60   for (int i = 0; i < (int)kNumElements; i++) {
61     ASSERT_EQ(i, list[i].x);
62     ASSERT_EQ(i, list[i].y);
63   }
64 }
65 
66 class MockDestructible {
67   USING_FAST_MALLOC(MockDestructible);
68 
69  public:
~MockDestructible()70   ~MockDestructible() { Destruct(); }
71   MOCK_METHOD0(Destruct, void());
72 };
73 
TEST(ContiguousContainerTest,DestructorCalled)74 TEST(ContiguousContainerTest, DestructorCalled) {
75   ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
76   auto& destructible = list.AllocateAndConstruct<MockDestructible>();
77   EXPECT_EQ(&destructible, &list.First());
78   EXPECT_CALL(destructible, Destruct());
79 }
80 
TEST(ContiguousContainerTest,DestructorCalledOnceWhenClear)81 TEST(ContiguousContainerTest, DestructorCalledOnceWhenClear) {
82   ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
83   auto& destructible = list.AllocateAndConstruct<MockDestructible>();
84   EXPECT_EQ(&destructible, &list.First());
85 
86   testing::MockFunction<void()> separator;
87   {
88     testing::InSequence s;
89     EXPECT_CALL(destructible, Destruct());
90     EXPECT_CALL(separator, Call());
91     EXPECT_CALL(destructible, Destruct()).Times(0);
92   }
93 
94   list.Clear();
95   separator.Call();
96 }
97 
TEST(ContiguousContainerTest,DestructorCalledOnceWhenRemoveLast)98 TEST(ContiguousContainerTest, DestructorCalledOnceWhenRemoveLast) {
99   ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
100   auto& destructible = list.AllocateAndConstruct<MockDestructible>();
101   EXPECT_EQ(&destructible, &list.First());
102 
103   testing::MockFunction<void()> separator;
104   {
105     testing::InSequence s;
106     EXPECT_CALL(destructible, Destruct());
107     EXPECT_CALL(separator, Call());
108     EXPECT_CALL(destructible, Destruct()).Times(0);
109   }
110 
111   list.RemoveLast();
112   separator.Call();
113 }
114 
TEST(ContiguousContainerTest,DestructorCalledWithMultipleRemoveLastCalls)115 TEST(ContiguousContainerTest, DestructorCalledWithMultipleRemoveLastCalls) {
116   // This container only requests space for one, but the implementation is
117   // free to use more space if the allocator provides it.
118   ContiguousContainer<MockDestructible> list(sizeof(MockDestructible),
119                                              1 * sizeof(MockDestructible));
120   testing::MockFunction<void()> separator;
121 
122   // We should be okay to allocate and remove a single one, like before.
123   list.AllocateAndConstruct<MockDestructible>();
124   EXPECT_EQ(1u, list.size());
125   {
126     testing::InSequence s;
127     EXPECT_CALL(list[0], Destruct());
128     EXPECT_CALL(separator, Call());
129     EXPECT_CALL(list[0], Destruct()).Times(0);
130   }
131   list.RemoveLast();
132   separator.Call();
133   EXPECT_EQ(0u, list.size());
134 
135   testing::Mock::VerifyAndClearExpectations(&separator);
136 
137   // We should also be okay to allocate and remove multiple.
138   list.AllocateAndConstruct<MockDestructible>();
139   list.AllocateAndConstruct<MockDestructible>();
140   list.AllocateAndConstruct<MockDestructible>();
141   list.AllocateAndConstruct<MockDestructible>();
142   list.AllocateAndConstruct<MockDestructible>();
143   list.AllocateAndConstruct<MockDestructible>();
144   EXPECT_EQ(6u, list.size());
145   {
146     // The last three should be destroyed by removeLast.
147     testing::InSequence s;
148     EXPECT_CALL(list[5], Destruct());
149     EXPECT_CALL(separator, Call());
150     EXPECT_CALL(list[5], Destruct()).Times(0);
151     EXPECT_CALL(list[4], Destruct());
152     EXPECT_CALL(separator, Call());
153     EXPECT_CALL(list[4], Destruct()).Times(0);
154     EXPECT_CALL(list[3], Destruct());
155     EXPECT_CALL(separator, Call());
156     EXPECT_CALL(list[3], Destruct()).Times(0);
157   }
158   list.RemoveLast();
159   separator.Call();
160   list.RemoveLast();
161   separator.Call();
162   list.RemoveLast();
163   separator.Call();
164   EXPECT_EQ(3u, list.size());
165 
166   // The remaining ones are destroyed when the test finishes.
167   EXPECT_CALL(list[2], Destruct());
168   EXPECT_CALL(list[1], Destruct());
169   EXPECT_CALL(list[0], Destruct());
170 }
171 
TEST(ContiguousContainerTest,InsertionAndIndexedAccess)172 TEST(ContiguousContainerTest, InsertionAndIndexedAccess) {
173   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
174 
175   auto& point1 = list.AllocateAndConstruct<Point2D>();
176   auto& point2 = list.AllocateAndConstruct<Point2D>();
177   auto& point3 = list.AllocateAndConstruct<Point2D>();
178 
179   EXPECT_EQ(3u, list.size());
180   EXPECT_EQ(&point1, &list.First());
181   EXPECT_EQ(&point3, &list.Last());
182   EXPECT_EQ(&point1, &list[0]);
183   EXPECT_EQ(&point2, &list[1]);
184   EXPECT_EQ(&point3, &list[2]);
185 }
186 
TEST(ContiguousContainerTest,InsertionAndClear)187 TEST(ContiguousContainerTest, InsertionAndClear) {
188   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
189   EXPECT_TRUE(list.IsEmpty());
190   EXPECT_EQ(0u, list.size());
191 
192   list.AllocateAndConstruct<Point2D>();
193   EXPECT_FALSE(list.IsEmpty());
194   EXPECT_EQ(1u, list.size());
195 
196   list.Clear();
197   EXPECT_TRUE(list.IsEmpty());
198   EXPECT_EQ(0u, list.size());
199 
200   list.AllocateAndConstruct<Point2D>();
201   EXPECT_FALSE(list.IsEmpty());
202   EXPECT_EQ(1u, list.size());
203 }
204 
TEST(ContiguousContainerTest,ElementAddressesAreStable)205 TEST(ContiguousContainerTest, ElementAddressesAreStable) {
206   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
207   Vector<Point2D*> pointers;
208   for (int i = 0; i < (int)kNumElements; i++)
209     pointers.push_back(&list.AllocateAndConstruct<Point2D>());
210   EXPECT_EQ(kNumElements, list.size());
211   EXPECT_EQ(kNumElements, pointers.size());
212 
213   auto list_it = list.begin();
214   auto** vector_it = pointers.begin();
215   for (; list_it != list.end(); ++list_it, ++vector_it)
216     EXPECT_EQ(&*list_it, *vector_it);
217 }
218 
TEST(ContiguousContainerTest,ForwardIteration)219 TEST(ContiguousContainerTest, ForwardIteration) {
220   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
221   for (int i = 0; i < (int)kNumElements; i++)
222     list.AllocateAndConstruct<Point2D>(i, i);
223   unsigned count = 0;
224   for (Point2D& point : list) {
225     EXPECT_EQ((int)count, point.x);
226     count++;
227   }
228   EXPECT_EQ(kNumElements, count);
229 
230   static_assert(std::is_same<decltype(*list.begin()), Point2D&>::value,
231                 "Non-const iteration should produce non-const references.");
232 }
233 
TEST(ContiguousContainerTest,ConstForwardIteration)234 TEST(ContiguousContainerTest, ConstForwardIteration) {
235   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
236   for (int i = 0; i < (int)kNumElements; i++)
237     list.AllocateAndConstruct<Point2D>(i, i);
238 
239   const auto& const_list = list;
240   unsigned count = 0;
241   for (const Point2D& point : const_list) {
242     EXPECT_EQ((int)count, point.x);
243     count++;
244   }
245   EXPECT_EQ(kNumElements, count);
246 
247   static_assert(
248       std::is_same<decltype(*const_list.begin()), const Point2D&>::value,
249       "Const iteration should produce const references.");
250 }
251 
TEST(ContiguousContainerTest,ReverseIteration)252 TEST(ContiguousContainerTest, ReverseIteration) {
253   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
254   for (int i = 0; i < (int)kNumElements; i++)
255     list.AllocateAndConstruct<Point2D>(i, i);
256 
257   unsigned count = 0;
258   for (auto it = list.rbegin(); it != list.rend(); ++it) {
259     EXPECT_EQ((int)(kNumElements - 1 - count), it->x);
260     count++;
261   }
262   EXPECT_EQ(kNumElements, count);
263 
264   static_assert(std::is_same<decltype(*list.rbegin()), Point2D&>::value,
265                 "Non-const iteration should produce non-const references.");
266 }
267 
268 // Checks that the latter list has pointers to the elements of the former.
269 template <typename It1, typename It2>
EqualPointers(It1 it1,const It1 & end1,It2 it2)270 bool EqualPointers(It1 it1, const It1& end1, It2 it2) {
271   for (; it1 != end1; ++it1, ++it2) {
272     if (&*it1 != *it2)
273       return false;
274   }
275   return true;
276 }
277 
TEST(ContiguousContainerTest,IterationAfterRemoveLast)278 TEST(ContiguousContainerTest, IterationAfterRemoveLast) {
279   struct SmallStruct {
280     char dummy[16];
281   };
282   ContiguousContainer<SmallStruct> list(sizeof(SmallStruct),
283                                         1 * sizeof(SmallStruct));
284   Vector<SmallStruct*> pointers;
285 
286   // Utilities which keep these two lists in sync and check that their
287   // iteration order matches.
288   auto push = [&list, &pointers]() {
289     pointers.push_back(&list.AllocateAndConstruct<SmallStruct>());
290   };
291   auto pop = [&list, &pointers]() {
292     pointers.pop_back();
293     list.RemoveLast();
294   };
295   auto check_equal = [&list, &pointers]() {
296     // They should be of the same size, and compare equal with all four
297     // kinds of iteration.
298     const auto& const_list = list;
299     const auto& const_pointers = pointers;
300     ASSERT_EQ(list.size(), pointers.size());
301     ASSERT_TRUE(EqualPointers(list.begin(), list.end(), pointers.begin()));
302     ASSERT_TRUE(EqualPointers(const_list.begin(), const_list.end(),
303                               const_pointers.begin()));
304     ASSERT_TRUE(EqualPointers(list.rbegin(), list.rend(), pointers.rbegin()));
305     ASSERT_TRUE(EqualPointers(const_list.rbegin(), const_list.rend(),
306                               const_pointers.rbegin()));
307   };
308 
309   // Note that the allocations that actually happen may not match the
310   // idealized descriptions here, since the implementation takes advantage of
311   // space available in the underlying allocator.
312   check_equal();  // Initially empty.
313   push();
314   check_equal();  // One full inner list.
315   push();
316   check_equal();  // One full, one partially full.
317   push();
318   push();
319   check_equal();  // Two full, one partially full.
320   pop();
321   check_equal();  // Two full, one empty.
322   pop();
323   check_equal();  // One full, one partially full, one empty.
324   pop();
325   check_equal();  // One full, one empty.
326   push();
327   pop();
328   pop();
329   ASSERT_TRUE(list.IsEmpty());
330   check_equal();  // Empty.
331 }
332 
TEST(ContiguousContainerTest,AppendByMovingSameList)333 TEST(ContiguousContainerTest, AppendByMovingSameList) {
334   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
335   list.AllocateAndConstruct<Point3D>(1, 2, 3);
336 
337   // Moves the Point3D to the end, and default-constructs a Point2D in its
338   // place.
339   list.AppendByMoving(list.First(), sizeof(Point3D));
340   EXPECT_EQ(1, list.Last().x);
341   EXPECT_EQ(2, list.Last().y);
342   EXPECT_EQ(3, static_cast<const Point3D&>(list.Last()).z);
343   EXPECT_EQ(2u, list.size());
344 
345   // Moves that Point2D to the end, and default-constructs another in its
346   // place.
347   list.First().x = 4;
348   list.AppendByMoving(list.First(), sizeof(Point2D));
349   EXPECT_EQ(4, list.Last().x);
350   EXPECT_EQ(3u, list.size());
351 }
352 
TEST(ContiguousContainerTest,AppendByMovingDoesNotDestruct)353 TEST(ContiguousContainerTest, AppendByMovingDoesNotDestruct) {
354   // GMock mock objects (e.g. MockDestructible) aren't guaranteed to be safe
355   // to memcpy (which is required for appendByMoving).
356   class DestructionNotifier {
357     USING_FAST_MALLOC(DestructionNotifier);
358 
359    public:
360     DestructionNotifier(bool* flag = nullptr) : flag_(flag) {}
361     ~DestructionNotifier() {
362       if (flag_)
363         *flag_ = true;
364     }
365 
366    private:
367     bool* flag_;
368   };
369 
370   bool destroyed = false;
371   ContiguousContainer<DestructionNotifier> list1(sizeof(DestructionNotifier));
372   list1.AllocateAndConstruct<DestructionNotifier>(&destroyed);
373   {
374     // Make sure destructor isn't called during appendByMoving.
375     ContiguousContainer<DestructionNotifier> list2(sizeof(DestructionNotifier));
376     list2.AppendByMoving(list1.Last(), sizeof(DestructionNotifier));
377     EXPECT_FALSE(destroyed);
378   }
379   // But it should be destroyed when list2 is.
380   EXPECT_TRUE(destroyed);
381 }
382 
TEST(ContiguousContainerTest,AppendByMovingReturnsMovedPointer)383 TEST(ContiguousContainerTest, AppendByMovingReturnsMovedPointer) {
384   ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
385   ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
386 
387   Point2D& point = list1.AllocateAndConstruct<Point2D>();
388   Point2D& moved_point1 = list2.AppendByMoving(point, sizeof(Point2D));
389   EXPECT_EQ(&moved_point1, &list2.Last());
390 
391   Point2D& moved_point2 = list1.AppendByMoving(moved_point1, sizeof(Point2D));
392   EXPECT_EQ(&moved_point2, &list1.Last());
393   EXPECT_NE(&moved_point1, &moved_point2);
394 }
395 
TEST(ContiguousContainerTest,AppendByMovingReplacesSourceWithNewElement)396 TEST(ContiguousContainerTest, AppendByMovingReplacesSourceWithNewElement) {
397   ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
398   ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
399 
400   list1.AllocateAndConstruct<Point2D>(1, 2);
401   EXPECT_EQ(1, list1.First().x);
402   EXPECT_EQ(2, list1.First().y);
403 
404   list2.AppendByMoving(list1.First(), sizeof(Point2D));
405   EXPECT_EQ(0, list1.First().x);
406   EXPECT_EQ(0, list1.First().y);
407   EXPECT_EQ(1, list2.First().x);
408   EXPECT_EQ(2, list2.First().y);
409 
410   EXPECT_EQ(1u, list1.size());
411   EXPECT_EQ(1u, list2.size());
412 }
413 
TEST(ContiguousContainerTest,AppendByMovingElementsOfDifferentSizes)414 TEST(ContiguousContainerTest, AppendByMovingElementsOfDifferentSizes) {
415   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
416   list.AllocateAndConstruct<Point3D>(1, 2, 3);
417   list.AllocateAndConstruct<Point2D>(4, 5);
418 
419   EXPECT_EQ(1, list[0].x);
420   EXPECT_EQ(2, list[0].y);
421   EXPECT_EQ(3, static_cast<const Point3D&>(list[0]).z);
422   EXPECT_EQ(4, list[1].x);
423   EXPECT_EQ(5, list[1].y);
424 
425   // Test that moving the first element actually moves the entire object, not
426   // just the base element.
427   list.AppendByMoving(list[0], sizeof(Point3D));
428   EXPECT_EQ(1, list[2].x);
429   EXPECT_EQ(2, list[2].y);
430   EXPECT_EQ(3, static_cast<const Point3D&>(list[2]).z);
431   EXPECT_EQ(4, list[1].x);
432   EXPECT_EQ(5, list[1].y);
433 
434   list.AppendByMoving(list[1], sizeof(Point2D));
435   EXPECT_EQ(1, list[2].x);
436   EXPECT_EQ(2, list[2].y);
437   EXPECT_EQ(3, static_cast<const Point3D&>(list[2]).z);
438   EXPECT_EQ(4, list[3].x);
439   EXPECT_EQ(5, list[3].y);
440 }
441 
TEST(ContiguousContainerTest,Swap)442 TEST(ContiguousContainerTest, Swap) {
443   ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize);
444   list1.AllocateAndConstruct<Point2D>(1, 2);
445   ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize);
446   list2.AllocateAndConstruct<Point2D>(3, 4);
447   list2.AllocateAndConstruct<Point2D>(5, 6);
448 
449   EXPECT_EQ(1u, list1.size());
450   EXPECT_EQ(1, list1[0].x);
451   EXPECT_EQ(2, list1[0].y);
452   EXPECT_EQ(2u, list2.size());
453   EXPECT_EQ(3, list2[0].x);
454   EXPECT_EQ(4, list2[0].y);
455   EXPECT_EQ(5, list2[1].x);
456   EXPECT_EQ(6, list2[1].y);
457 
458   list2.Swap(list1);
459 
460   EXPECT_EQ(1u, list2.size());
461   EXPECT_EQ(1, list2[0].x);
462   EXPECT_EQ(2, list2[0].y);
463   EXPECT_EQ(2u, list1.size());
464   EXPECT_EQ(3, list1[0].x);
465   EXPECT_EQ(4, list1[0].y);
466   EXPECT_EQ(5, list1[1].x);
467   EXPECT_EQ(6, list1[1].y);
468 }
469 
TEST(ContiguousContainerTest,CapacityInBytes)470 TEST(ContiguousContainerTest, CapacityInBytes) {
471   const int kIterations = 500;
472   const size_t kInitialCapacity = 10 * kMaxPointSize;
473   const size_t kUpperBoundOnMinCapacity = kInitialCapacity;
474 
475   // At time of writing, removing elements from the end can cause up to 7x the
476   // memory required to be consumed, in the worst case, since we can have up to
477   // two trailing inner lists that are empty (for 2*size + 4*size in unused
478   // memory, due to the exponential growth strategy).
479   // Unfortunately, this captures behaviour of the underlying allocator as
480   // well as this container, so we're pretty loose here. This constant may
481   // need to be adjusted.
482   const size_t kMaxWasteFactor = 8;
483 
484   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize,
485                                                      kInitialCapacity);
486 
487   // The capacity should grow with the list.
488   for (int i = 0; i < kIterations; i++) {
489     size_t capacity = list.CapacityInBytes();
490     ASSERT_GE(capacity, list.size() * sizeof(Point2D));
491     ASSERT_LE(capacity, std::max(list.size() * sizeof(Point2D),
492                                  kUpperBoundOnMinCapacity) *
493                             kMaxWasteFactor);
494     list.AllocateAndConstruct<Point2D>();
495   }
496 
497   // The capacity should shrink with the list.
498   for (int i = 0; i < kIterations; i++) {
499     size_t capacity = list.CapacityInBytes();
500     ASSERT_GE(capacity, list.size() * sizeof(Point2D));
501     ASSERT_LE(capacity, std::max(list.size() * sizeof(Point2D),
502                                  kUpperBoundOnMinCapacity) *
503                             kMaxWasteFactor);
504     list.RemoveLast();
505   }
506 }
507 
TEST(ContiguousContainerTest,CapacityInBytesAfterClear)508 TEST(ContiguousContainerTest, CapacityInBytesAfterClear) {
509   // Clearing should restore the capacity of the container to the same as a
510   // newly allocated one (without reserved capacity requested).
511   ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
512   size_t empty_capacity = list.CapacityInBytes();
513   list.AllocateAndConstruct<Point2D>();
514   list.AllocateAndConstruct<Point2D>();
515   list.Clear();
516   EXPECT_EQ(empty_capacity, list.CapacityInBytes());
517 }
518 
TEST(ContiguousContainerTest,Alignment)519 TEST(ContiguousContainerTest, Alignment) {
520   const size_t kMaxAlign = alignof(long double);
521   ContiguousContainer<Point2D, kMaxAlign> list(kMaxPointSize);
522 
523   list.AllocateAndConstruct<Point2D>();
524   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
525   list.AllocateAndConstruct<Point2D>();
526   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
527   list.AllocateAndConstruct<Point3D>();
528   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
529   list.AllocateAndConstruct<Point3D>();
530   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
531   list.AllocateAndConstruct<Point2D>();
532   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
533 
534   list.AppendByMoving(list[0], sizeof(Point2D));
535   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
536   list.AppendByMoving(list[1], sizeof(Point2D));
537   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
538   list.AppendByMoving(list[2], sizeof(Point3D));
539   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
540   list.AppendByMoving(list[3], sizeof(Point3D));
541   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
542   list.AppendByMoving(list[4], sizeof(Point2D));
543   EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.Last()) & (kMaxAlign - 1));
544 }
545 
546 }  // namespace
547 }  // namespace blink
548