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