1 // Copyright 2022 The Chromium Authors
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 "partition_alloc/compressed_pointer.h"
6
7 #include "partition_alloc/partition_alloc.h"
8 #include "partition_alloc/partition_alloc_buildflags.h"
9 #include "partition_alloc/partition_root.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace partition_alloc {
13
14 namespace {
15
16 struct Base {
17 double a;
18 };
19 struct Derived : Base {
20 double b;
21 };
22 struct Mixin {
23 double c;
24 };
25 struct DerivedWithMixin : Base, Mixin {
26 double d;
27 };
28
29 struct PADeleter final {
operator ()partition_alloc::__anon63aa54b60111::PADeleter30 void operator()(void* ptr) const { allocator_.root()->Free(ptr); }
31 PartitionAllocator& allocator_;
32 };
33
34 template <typename T, typename... Args>
make_pa_unique(PartitionAllocator & alloc,Args &&...args)35 std::unique_ptr<T, PADeleter> make_pa_unique(PartitionAllocator& alloc,
36 Args&&... args) {
37 T* result = new (alloc.root()->Alloc(sizeof(T), nullptr))
38 T(std::forward<Args>(args)...);
39 return std::unique_ptr<T, PADeleter>(result, PADeleter{alloc});
40 }
41
42 template <typename T>
make_pa_array_unique(PartitionAllocator & alloc,size_t num)43 std::unique_ptr<T[], PADeleter> make_pa_array_unique(PartitionAllocator& alloc,
44 size_t num) {
45 T* result = new (alloc.root()->Alloc(sizeof(T) * num, nullptr)) T();
46 return std::unique_ptr<T[], PADeleter>(result, PADeleter{alloc});
47 }
48
49 // Test that pointer types are trivial.
50 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
51 static_assert(
52 std::is_trivially_default_constructible_v<CompressedPointer<Base>>);
53 static_assert(std::is_trivially_copy_constructible_v<CompressedPointer<Base>>);
54 static_assert(std::is_trivially_move_constructible_v<CompressedPointer<Base>>);
55 static_assert(std::is_trivially_copy_assignable_v<CompressedPointer<Base>>);
56 static_assert(std::is_trivially_move_assignable_v<CompressedPointer<Base>>);
57 #endif // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
58 static_assert(
59 std::is_trivially_default_constructible_v<UncompressedPointer<Base>>);
60 static_assert(
61 std::is_trivially_copy_constructible_v<UncompressedPointer<Base>>);
62 static_assert(
63 std::is_trivially_move_constructible_v<UncompressedPointer<Base>>);
64 static_assert(std::is_trivially_copy_assignable_v<UncompressedPointer<Base>>);
65 static_assert(std::is_trivially_move_assignable_v<UncompressedPointer<Base>>);
66
67 } // namespace
68
69 struct UncompressedTypeTag {};
70 struct CompressedTypeTag {};
71
72 template <typename TagType>
73 class CompressedPointerTest : public ::testing::Test {
74 public:
75 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
76 template <typename T>
77 using PointerType =
78 std::conditional_t<std::is_same_v<TagType, CompressedTypeTag>,
79 CompressedPointer<T>,
80 UncompressedPointer<T>>;
81 #else // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
82 template <typename T>
83 using PointerType = UncompressedPointer<T>;
84 #endif // BUILDFLAG(ENABLE_POINTER_COMPRESSION)
85
CompressedPointerTest()86 CompressedPointerTest() : allocator_(PartitionOptions{}) {}
87
88 protected:
89 PartitionAllocator allocator_;
90 };
91
92 #if BUILDFLAG(ENABLE_POINTER_COMPRESSION)
93 using ObjectTypes = ::testing::Types<UncompressedTypeTag, CompressedTypeTag>;
94 #else
95 using ObjectTypes = ::testing::Types<UncompressedTypeTag>;
96 #endif
97
98 TYPED_TEST_SUITE(CompressedPointerTest, ObjectTypes);
99
TYPED_TEST(CompressedPointerTest,NullConstruction)100 TYPED_TEST(CompressedPointerTest, NullConstruction) {
101 using DoublePointer = typename TestFixture::template PointerType<double>;
102 {
103 DoublePointer p = static_cast<DoublePointer>(nullptr);
104 EXPECT_FALSE(p.is_nonnull());
105 EXPECT_FALSE(p.get());
106 EXPECT_EQ(p, nullptr);
107 }
108 {
109 DoublePointer p1 = static_cast<DoublePointer>(nullptr);
110 DoublePointer p2 = p1;
111 EXPECT_FALSE(p2.is_nonnull());
112 EXPECT_FALSE(p2.get());
113 EXPECT_EQ(p2, nullptr);
114 }
115 {
116 DoublePointer p1 = static_cast<DoublePointer>(nullptr);
117 DoublePointer p2 = std::move(p1);
118 EXPECT_FALSE(p2.is_nonnull());
119 EXPECT_FALSE(p2.get());
120 EXPECT_EQ(p2, nullptr);
121 }
122 }
123
TYPED_TEST(CompressedPointerTest,NullAssignment)124 TYPED_TEST(CompressedPointerTest, NullAssignment) {
125 using DoublePointer = typename TestFixture::template PointerType<double>;
126 {
127 DoublePointer p;
128 p = static_cast<DoublePointer>(nullptr);
129 EXPECT_FALSE(p.is_nonnull());
130 EXPECT_FALSE(p.get());
131 EXPECT_EQ(p.get(), nullptr);
132 EXPECT_EQ(p, nullptr);
133 }
134 {
135 DoublePointer p1 = DoublePointer(nullptr), p2;
136 p2 = p1;
137 EXPECT_FALSE(p2.is_nonnull());
138 EXPECT_FALSE(p2.get());
139 EXPECT_EQ(p2.get(), nullptr);
140 EXPECT_EQ(p2, nullptr);
141 }
142 {
143 DoublePointer p1 = DoublePointer(nullptr), p2;
144 p2 = std::move(p1);
145 EXPECT_FALSE(p2.is_nonnull());
146 EXPECT_FALSE(p2.get());
147 EXPECT_EQ(p2.get(), nullptr);
148 EXPECT_EQ(p2, nullptr);
149 }
150 }
151
TYPED_TEST(CompressedPointerTest,SameTypeValueConstruction)152 TYPED_TEST(CompressedPointerTest, SameTypeValueConstruction) {
153 using DoublePointer = typename TestFixture::template PointerType<double>;
154 auto d = make_pa_unique<double>(this->allocator_);
155 {
156 DoublePointer p = static_cast<DoublePointer>(d.get());
157 EXPECT_TRUE(p.is_nonnull());
158 EXPECT_EQ(p.get(), d.get());
159 EXPECT_EQ(p, d.get());
160 }
161 {
162 DoublePointer p1 = static_cast<DoublePointer>(d.get());
163 DoublePointer p2 = p1;
164 EXPECT_TRUE(p2.is_nonnull());
165 EXPECT_EQ(p2.get(), d.get());
166 EXPECT_EQ(p2, p1);
167 EXPECT_EQ(p2, d.get());
168 }
169 {
170 DoublePointer p1 = static_cast<DoublePointer>(d.get());
171 DoublePointer p2 = std::move(p1);
172 EXPECT_TRUE(p2.is_nonnull());
173 EXPECT_EQ(p2.get(), d.get());
174 EXPECT_EQ(p2, d.get());
175 }
176 }
177
TYPED_TEST(CompressedPointerTest,SameTypeValueAssignment)178 TYPED_TEST(CompressedPointerTest, SameTypeValueAssignment) {
179 using DoublePointer = typename TestFixture::template PointerType<double>;
180 auto d = make_pa_unique<double>(this->allocator_);
181 {
182 DoublePointer p;
183 p = static_cast<DoublePointer>(d.get());
184 EXPECT_TRUE(p.is_nonnull());
185 EXPECT_EQ(p.get(), d.get());
186 EXPECT_EQ(p, d.get());
187 }
188 {
189 DoublePointer p1 = static_cast<DoublePointer>(d.get());
190 DoublePointer p2;
191 p2 = p1;
192 EXPECT_TRUE(p2.is_nonnull());
193 EXPECT_EQ(p2.get(), d.get());
194 EXPECT_EQ(p2, p1);
195 EXPECT_EQ(p2, d.get());
196 }
197 {
198 DoublePointer p1 = static_cast<DoublePointer>(d.get());
199 DoublePointer p2;
200 p2 = std::move(p1);
201 EXPECT_TRUE(p2.is_nonnull());
202 EXPECT_EQ(p2.get(), d.get());
203 EXPECT_EQ(p2, d.get());
204 }
205 }
206
TYPED_TEST(CompressedPointerTest,HeterogeneousValueConstructionSamePointerValue)207 TYPED_TEST(CompressedPointerTest,
208 HeterogeneousValueConstructionSamePointerValue) {
209 using BasePointer = typename TestFixture::template PointerType<Base>;
210 auto d = make_pa_unique<Derived>(this->allocator_);
211 {
212 BasePointer p = static_cast<BasePointer>(d.get());
213 EXPECT_TRUE(p.is_nonnull());
214 EXPECT_EQ(p.get(), d.get());
215 }
216 {
217 BasePointer p1 = static_cast<BasePointer>(d.get());
218 BasePointer p2 = p1;
219 EXPECT_TRUE(p2.is_nonnull());
220 EXPECT_EQ(p2.get(), d.get());
221 EXPECT_EQ(p2, p1);
222 EXPECT_EQ(p2, d.get());
223 }
224 {
225 BasePointer p1 = static_cast<BasePointer>(d.get());
226 BasePointer p2 = std::move(p1);
227 EXPECT_TRUE(p2.is_nonnull());
228 EXPECT_EQ(p2.get(), d.get());
229 EXPECT_EQ(p2, d.get());
230 }
231 }
232
TYPED_TEST(CompressedPointerTest,HeterogeneousValueAssignmentSamePointerValue)233 TYPED_TEST(CompressedPointerTest,
234 HeterogeneousValueAssignmentSamePointerValue) {
235 using BasePointer = typename TestFixture::template PointerType<Base>;
236 auto d = make_pa_unique<Derived>(this->allocator_);
237 {
238 BasePointer p;
239 p = static_cast<BasePointer>(d.get());
240 EXPECT_TRUE(p.is_nonnull());
241 EXPECT_EQ(p.get(), d.get());
242 }
243 {
244 BasePointer p1 = static_cast<BasePointer>(d.get());
245 BasePointer p2;
246 p2 = p1;
247 EXPECT_TRUE(p2.is_nonnull());
248 EXPECT_EQ(p2.get(), d.get());
249 EXPECT_EQ(p2, p1);
250 EXPECT_EQ(p2, d.get());
251 }
252 {
253 BasePointer p1 = static_cast<BasePointer>(d.get());
254 BasePointer p2;
255 p2 = std::move(p1);
256 EXPECT_TRUE(p2.is_nonnull());
257 EXPECT_EQ(p2.get(), d.get());
258 EXPECT_EQ(p2, d.get());
259 }
260 }
261
TYPED_TEST(CompressedPointerTest,HeterogeneousValueConstructionDifferentPointerValues)262 TYPED_TEST(CompressedPointerTest,
263 HeterogeneousValueConstructionDifferentPointerValues) {
264 using MixinPointer = typename TestFixture::template PointerType<Mixin>;
265 auto d = make_pa_unique<DerivedWithMixin>(this->allocator_);
266 {
267 MixinPointer p = static_cast<MixinPointer>(d.get());
268 ASSERT_NE(static_cast<void*>(p.get()), static_cast<void*>(d.get()));
269 }
270 {
271 MixinPointer p = static_cast<MixinPointer>(d.get());
272 EXPECT_TRUE(p.is_nonnull());
273 EXPECT_EQ(p.get(), d.get());
274 }
275 {
276 MixinPointer p1 = static_cast<MixinPointer>(d.get());
277 MixinPointer p2 = p1;
278 EXPECT_TRUE(p2.is_nonnull());
279 EXPECT_EQ(p2.get(), d.get());
280 EXPECT_EQ(p2, p1);
281 EXPECT_EQ(p2, d.get());
282 }
283 {
284 MixinPointer p1 = static_cast<MixinPointer>(d.get());
285 MixinPointer p2 = std::move(p1);
286 EXPECT_TRUE(p2.is_nonnull());
287 EXPECT_EQ(p2.get(), d.get());
288 EXPECT_EQ(p2, d.get());
289 }
290 }
291
TYPED_TEST(CompressedPointerTest,HeterogeneousValueAssignmentDifferentPointerValue)292 TYPED_TEST(CompressedPointerTest,
293 HeterogeneousValueAssignmentDifferentPointerValue) {
294 using MixinPointer = typename TestFixture::template PointerType<Mixin>;
295 auto d = make_pa_unique<DerivedWithMixin>(this->allocator_);
296 {
297 MixinPointer p;
298 p = static_cast<MixinPointer>(d.get());
299 ASSERT_NE(static_cast<void*>(p.get()), static_cast<void*>(d.get()));
300 }
301 {
302 MixinPointer p;
303 p = static_cast<MixinPointer>(d.get());
304 EXPECT_TRUE(p.is_nonnull());
305 EXPECT_EQ(p.get(), d.get());
306 }
307 {
308 MixinPointer p1 = static_cast<MixinPointer>(d.get());
309 MixinPointer p2;
310 p2 = p1;
311 EXPECT_TRUE(p2.is_nonnull());
312 EXPECT_EQ(p2.get(), d.get());
313 EXPECT_EQ(p2, p1);
314 EXPECT_EQ(p2, d.get());
315 }
316 {
317 MixinPointer p1 = static_cast<MixinPointer>(d.get());
318 MixinPointer p2;
319 p2 = std::move(p1);
320 EXPECT_TRUE(p2.is_nonnull());
321 EXPECT_EQ(p2.get(), d.get());
322 EXPECT_EQ(p2, d.get());
323 }
324 }
325
326 namespace {
327
328 template <template <typename> class PointerType,
329 typename T1,
330 typename T2,
331 typename U>
EqualityTest(U * raw)332 void EqualityTest(U* raw) {
333 PointerType<T1> p1 = static_cast<PointerType<T1>>(raw);
334 PointerType<T2> p2 = static_cast<PointerType<T2>>(raw);
335 EXPECT_EQ(p1, raw);
336 EXPECT_EQ(p2, raw);
337 EXPECT_EQ(raw, p1);
338 EXPECT_EQ(raw, p2);
339 EXPECT_EQ(p1, p2);
340 }
341
342 template <template <typename> class PointerType,
343 typename T1,
344 typename T2,
345 typename U>
CompareTest(U * array)346 void CompareTest(U* array) {
347 PointerType<T1> p0 = static_cast<PointerType<T1>>(&array[0]);
348 PointerType<T2> p1 = static_cast<PointerType<T2>>(&array[1]);
349 {
350 EXPECT_NE(p0, &array[1]);
351 EXPECT_NE(p0, p1);
352 EXPECT_NE(p1, &array[0]);
353 EXPECT_NE(p1, p0);
354 }
355 {
356 EXPECT_LT(p0, &array[1]);
357 EXPECT_LT(&array[0], p1);
358 EXPECT_LT(p0, p1);
359 }
360 {
361 EXPECT_LE(p0, &array[0]);
362 EXPECT_LE(p0, &array[1]);
363 EXPECT_LE(&array[0], p0);
364
365 EXPECT_LE(&array[1], p1);
366 EXPECT_LE(p1, &array[1]);
367
368 auto p2 = p0;
369 EXPECT_LE(p0, p2);
370 EXPECT_LE(p2, p1);
371 }
372 {
373 EXPECT_GT(&array[1], p0);
374 EXPECT_GT(p1, &array[0]);
375 EXPECT_GT(p1, p0);
376 }
377 {
378 EXPECT_GE(&array[0], p0);
379 EXPECT_GE(&array[1], p0);
380 EXPECT_GE(p0, &array[0]);
381
382 EXPECT_GE(p1, &array[1]);
383 EXPECT_GE(&array[1], p1);
384
385 auto p2 = p1;
386 EXPECT_GE(p1, p2);
387 EXPECT_GE(p1, p0);
388 }
389 }
390
391 } // namespace
392
TYPED_TEST(CompressedPointerTest,EqualitySamePointerValue)393 TYPED_TEST(CompressedPointerTest, EqualitySamePointerValue) {
394 auto d = make_pa_unique<Derived>(this->allocator_);
395 EqualityTest<TestFixture::template PointerType, Base, Base>(d.get());
396 EqualityTest<TestFixture::template PointerType, Base, Derived>(d.get());
397 EqualityTest<TestFixture::template PointerType, Derived, Base>(d.get());
398 EqualityTest<TestFixture::template PointerType, Derived, Derived>(d.get());
399 }
400
TYPED_TEST(CompressedPointerTest,EqualityDifferentPointerValues)401 TYPED_TEST(CompressedPointerTest, EqualityDifferentPointerValues) {
402 auto d = make_pa_unique<DerivedWithMixin>(this->allocator_);
403 EqualityTest<TestFixture::template PointerType, Mixin, Mixin>(d.get());
404 EqualityTest<TestFixture::template PointerType, Mixin, DerivedWithMixin>(
405 d.get());
406 EqualityTest<TestFixture::template PointerType, DerivedWithMixin, Mixin>(
407 d.get());
408 EqualityTest<TestFixture::template PointerType, DerivedWithMixin,
409 DerivedWithMixin>(d.get());
410 }
411
TYPED_TEST(CompressedPointerTest,CompareSamePointerValue)412 TYPED_TEST(CompressedPointerTest, CompareSamePointerValue) {
413 auto d = make_pa_array_unique<Derived>(this->allocator_, 2);
414 CompareTest<TestFixture::template PointerType, Base, Base>(d.get());
415 CompareTest<TestFixture::template PointerType, Base, Derived>(d.get());
416 CompareTest<TestFixture::template PointerType, Derived, Base>(d.get());
417 CompareTest<TestFixture::template PointerType, Derived, Derived>(d.get());
418 }
419
TYPED_TEST(CompressedPointerTest,CompareDifferentPointerValues)420 TYPED_TEST(CompressedPointerTest, CompareDifferentPointerValues) {
421 auto d = make_pa_array_unique<DerivedWithMixin>(this->allocator_, 2);
422 CompareTest<TestFixture::template PointerType, Mixin, Mixin>(d.get());
423 CompareTest<TestFixture::template PointerType, Mixin, DerivedWithMixin>(
424 d.get());
425 CompareTest<TestFixture::template PointerType, DerivedWithMixin, Mixin>(
426 d.get());
427 CompareTest<TestFixture::template PointerType, DerivedWithMixin,
428 DerivedWithMixin>(d.get());
429 }
430
431 } // namespace partition_alloc
432