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