• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // 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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_allocator/unique_ptr.h"
16 
17 #include <cstddef>
18 
19 #include "pw_allocator/allocator.h"
20 #include "pw_allocator/internal/managed_ptr_testing.h"
21 #include "pw_unit_test/framework.h"
22 
23 namespace {
24 
25 using pw::allocator::test::Counter;
26 using pw::allocator::test::CounterSink;
27 using pw::allocator::test::CounterWithBuffer;
28 using UniquePtrTest = pw::allocator::test::ManagedPtrTest;
29 
TEST_F(UniquePtrTest,DefaultInitializationIsNullptr)30 TEST_F(UniquePtrTest, DefaultInitializationIsNullptr) {
31   pw::UniquePtr<int> empty;
32   EXPECT_EQ(empty.get(), nullptr);
33 }
34 
TEST_F(UniquePtrTest,OperatorEqNullptrOnEmptyUniquePtrSucceeds)35 TEST_F(UniquePtrTest, OperatorEqNullptrOnEmptyUniquePtrSucceeds) {
36   pw::UniquePtr<int> empty;
37   EXPECT_TRUE(empty == nullptr);
38   EXPECT_FALSE(empty != nullptr);
39 }
40 
TEST_F(UniquePtrTest,OperatorEqNullptrAfterMakeUniqueFails)41 TEST_F(UniquePtrTest, OperatorEqNullptrAfterMakeUniqueFails) {
42   auto ptr = allocator_.MakeUnique<int>(5);
43   EXPECT_TRUE(ptr != nullptr);
44   EXPECT_FALSE(ptr == nullptr);
45 }
46 
TEST_F(UniquePtrTest,OperatorEqNullptrAfterMakeUniqueNullptrTypeFails)47 TEST_F(UniquePtrTest, OperatorEqNullptrAfterMakeUniqueNullptrTypeFails) {
48   auto ptr = allocator_.MakeUnique<std::nullptr_t>(nullptr);
49   EXPECT_TRUE(ptr != nullptr);
50   EXPECT_FALSE(ptr == nullptr);
51   EXPECT_TRUE(*ptr == nullptr);
52   EXPECT_FALSE(*ptr != nullptr);
53 }
54 
TEST_F(UniquePtrTest,MakeUniqueForwardsConstructorArguments)55 TEST_F(UniquePtrTest, MakeUniqueForwardsConstructorArguments) {
56   Counter counter(6);
57   auto ptr = allocator_.MakeUnique<CounterSink>(std::move(counter));
58   ASSERT_NE(ptr, nullptr);
59   EXPECT_EQ(ptr->value(), 6u);
60 }
61 
TEST_F(UniquePtrTest,MoveConstructsFromSubClassAndFreesTotalSize)62 TEST_F(UniquePtrTest, MoveConstructsFromSubClassAndFreesTotalSize) {
63   auto ptr = allocator_.MakeUnique<CounterWithBuffer>();
64   ASSERT_NE(ptr, nullptr);
65   EXPECT_EQ(allocator_.allocate_size(), sizeof(CounterWithBuffer));
66   pw::UniquePtr<Counter> base_ptr(std::move(ptr));
67 
68   EXPECT_EQ(allocator_.deallocate_size(), 0ul);
69   // The size that is deallocated here should be the size of the larger
70   // subclass, not the size of the smaller base class.
71   base_ptr.Reset();
72   EXPECT_EQ(allocator_.deallocate_size(), sizeof(CounterWithBuffer));
73 }
74 
TEST_F(UniquePtrTest,MoveAssignsFromSubClassAndFreesTotalSize)75 TEST_F(UniquePtrTest, MoveAssignsFromSubClassAndFreesTotalSize) {
76   auto ptr = allocator_.MakeUnique<CounterWithBuffer>();
77   ASSERT_NE(ptr, nullptr);
78   EXPECT_EQ(allocator_.allocate_size(), sizeof(CounterWithBuffer));
79   pw::UniquePtr<Counter> base_ptr = std::move(ptr);
80 
81   EXPECT_EQ(allocator_.deallocate_size(), 0ul);
82   // The size that is deallocated here should be the size of the larger
83   // subclass, not the size of the smaller base class.
84   base_ptr.Reset();
85   EXPECT_EQ(allocator_.deallocate_size(), sizeof(CounterWithBuffer));
86 }
87 
TEST_F(UniquePtrTest,MoveAssignsToExistingDeallocates)88 TEST_F(UniquePtrTest, MoveAssignsToExistingDeallocates) {
89   auto size1 = allocator_.MakeUnique<size_t>(1);
90   ASSERT_NE(size1, nullptr);
91   EXPECT_EQ(*size1, 1U);
92 
93   auto size2 = allocator_.MakeUnique<size_t>(2);
94   ASSERT_NE(size1, nullptr);
95   EXPECT_EQ(*size2, 2U);
96 
97   EXPECT_EQ(allocator_.deallocate_size(), 0U);
98   size1 = std::move(size2);
99   EXPECT_EQ(allocator_.deallocate_size(), sizeof(size_t));
100   EXPECT_EQ(*size1, 2U);
101 }
102 
TEST_F(UniquePtrTest,DestructorDestroysAndFrees)103 TEST_F(UniquePtrTest, DestructorDestroysAndFrees) {
104   auto ptr = allocator_.MakeUnique<Counter>();
105   ASSERT_NE(ptr, nullptr);
106   EXPECT_EQ(Counter::GetNumDtorCalls(), 0u);
107   EXPECT_EQ(allocator_.deallocate_size(), 0ul);
108 
109   ptr.Reset();  // Reset the UniquePtr, destroying its contents.
110   EXPECT_EQ(Counter::GetNumDtorCalls(), 1u);
111   EXPECT_EQ(allocator_.deallocate_size(), sizeof(Counter));
112 }
113 
TEST_F(UniquePtrTest,ArrayElementsAreConstructed)114 TEST_F(UniquePtrTest, ArrayElementsAreConstructed) {
115   constexpr static size_t kArraySize = 5;
116 
117   // TODO(b/326509341): Remove when downstream consumers migrate.
118   // Use the deprecated method...
119   auto ptr1 = allocator_.MakeUniqueArray<Counter>(kArraySize);
120   ASSERT_NE(ptr1, nullptr);
121   EXPECT_EQ(Counter::GetNumCtorCalls(), kArraySize);
122   for (size_t i = 0; i < kArraySize; ++i) {
123     EXPECT_EQ(ptr1[i].value(), i);
124   }
125 
126   // ...and the supported method.
127   auto ptr2 = allocator_.MakeUnique<Counter[]>(kArraySize);
128   EXPECT_EQ(Counter::GetNumCtorCalls(), kArraySize);
129   ASSERT_NE(ptr2, nullptr);
130   for (size_t i = 0; i < kArraySize; ++i) {
131     EXPECT_EQ(ptr2[i].value(), i);
132   }
133 }
134 
TEST_F(UniquePtrTest,ArrayElementsAreConstructedWithSpecifiedAlignment)135 TEST_F(UniquePtrTest, ArrayElementsAreConstructedWithSpecifiedAlignment) {
136   constexpr static size_t kArraySize = 5;
137   constexpr static size_t kArrayAlignment = 32;
138 
139   // TODO(b/326509341): Remove when downstream consumers migrate.
140   // Use the deprecated method...
141   auto ptr1 = allocator_.MakeUniqueArray<Counter>(kArraySize, kArrayAlignment);
142   ASSERT_NE(ptr1, nullptr);
143   EXPECT_EQ(Counter::GetNumCtorCalls(), kArraySize);
144 
145   auto addr1 = reinterpret_cast<uintptr_t>(ptr1.get());
146   EXPECT_EQ(addr1 % kArrayAlignment, 0u);
147 
148   // ...and the supported method.
149   auto ptr2 = allocator_.MakeUnique<Counter[]>(kArraySize, kArrayAlignment);
150   ASSERT_NE(ptr2, nullptr);
151   EXPECT_EQ(Counter::GetNumCtorCalls(), kArraySize);
152 
153   auto addr2 = reinterpret_cast<uintptr_t>(ptr2.get());
154   EXPECT_EQ(addr2 % kArrayAlignment, 0u);
155 }
156 
TEST_F(UniquePtrTest,DestructorDestroysAndFreesArray)157 TEST_F(UniquePtrTest, DestructorDestroysAndFreesArray) {
158   constexpr static size_t kArraySize = 5;
159 
160   auto ptr = allocator_.MakeUnique<Counter[]>(kArraySize);
161   ASSERT_NE(ptr, nullptr);
162   EXPECT_EQ(Counter::GetNumDtorCalls(), 0u);
163   EXPECT_EQ(allocator_.deallocate_size(), 0ul);
164 
165   ptr.Reset();  // Reset the UniquePtr, destroying its contents.
166   EXPECT_EQ(Counter::GetNumDtorCalls(), kArraySize);
167   EXPECT_EQ(allocator_.deallocate_size(), sizeof(Counter) * kArraySize);
168 }
169 
TEST_F(UniquePtrTest,CanRelease)170 TEST_F(UniquePtrTest, CanRelease) {
171   size_t* raw = nullptr;
172   {
173     auto ptr = allocator_.MakeUnique<size_t>(1);
174     ASSERT_NE(ptr, nullptr);
175     EXPECT_EQ(ptr.deallocator(), &allocator_);
176     raw = ptr.Release();
177 
178     // Allocator pointer parameter is optional. Re-releasing returns null.
179     EXPECT_EQ(ptr.Release(), nullptr);
180   }
181 
182   // Deallocate should not be called, even though UniquePtr goes out of scope.
183   EXPECT_EQ(allocator_.deallocate_size(), 0U);
184   allocator_.Delete(raw);
185   EXPECT_EQ(allocator_.deallocate_size(), sizeof(size_t));
186 }
187 
TEST_F(UniquePtrTest,SizeReturnsCorrectSize)188 TEST_F(UniquePtrTest, SizeReturnsCorrectSize) {
189   auto ptr_array = allocator_.MakeUnique<int[]>(5);
190   EXPECT_EQ(ptr_array.size(), 5U);
191 }
192 
TEST_F(UniquePtrTest,SizeReturnsCorrectSizeWhenAligned)193 TEST_F(UniquePtrTest, SizeReturnsCorrectSizeWhenAligned) {
194   auto ptr_array = allocator_.MakeUnique<int[]>(5, 32);
195   EXPECT_EQ(ptr_array.size(), 5U);
196 }
197 
TEST_F(UniquePtrTest,CanSwapWhenNeitherAreEmpty)198 TEST_F(UniquePtrTest, CanSwapWhenNeitherAreEmpty) {
199   auto ptr1 = allocator_.MakeUnique<Counter>(111);
200   auto ptr2 = allocator_.MakeUnique<Counter>(222);
201   ptr1.Swap(ptr2);
202   EXPECT_EQ(ptr1->value(), 222u);
203   EXPECT_EQ(ptr2->value(), 111u);
204 }
205 
TEST_F(UniquePtrTest,CanSwapWhenOneIsEmpty)206 TEST_F(UniquePtrTest, CanSwapWhenOneIsEmpty) {
207   auto ptr1 = allocator_.MakeUnique<Counter>(111);
208   pw::UniquePtr<Counter> ptr2;
209 
210   // ptr2 is empty.
211   ptr1.Swap(ptr2);
212   EXPECT_EQ(ptr2->value(), 111u);
213   EXPECT_EQ(ptr1, nullptr);
214 
215   // ptr1 is empty.
216   ptr1.Swap(ptr2);
217   EXPECT_EQ(ptr1->value(), 111u);
218   EXPECT_EQ(ptr2, nullptr);
219 }
220 
TEST_F(UniquePtrTest,CanSwapWhenBothAreEmpty)221 TEST_F(UniquePtrTest, CanSwapWhenBothAreEmpty) {
222   pw::UniquePtr<Counter> ptr1;
223   pw::UniquePtr<Counter> ptr2;
224   ptr1.Swap(ptr2);
225   EXPECT_EQ(ptr1, nullptr);
226   EXPECT_EQ(ptr2, nullptr);
227 }
228 
229 class UniquePtrTestAllocator
230     : public pw::allocator::test::AllocatorForTest<256> {
231  public:
232   template <typename T>
MakeBespokeArray(size_t size)233   pw::UniquePtr<T[]> MakeBespokeArray(size_t size) {
234     return Deallocator::WrapUniqueArray<T>(New<T[]>(size), size);
235   }
236 };
237 
TEST_F(UniquePtrTest,DeprecatedWrapUniqueArrayStillWorks)238 TEST_F(UniquePtrTest, DeprecatedWrapUniqueArrayStillWorks) {
239   constexpr static size_t kArraySize = 5;
240   UniquePtrTestAllocator allocator;
241   {
242     auto ptr = allocator.MakeBespokeArray<Counter>(kArraySize);
243     ASSERT_NE(ptr, nullptr);
244     EXPECT_EQ(Counter::GetNumCtorCalls(), kArraySize);
245   }
246   EXPECT_EQ(Counter::GetNumDtorCalls(), kArraySize);
247 }
248 
249 // Verify that the UniquePtr implementation is the size of 2 pointers for the
250 // non-array case. This should not contain the size_t size_ parameter.
251 static_assert(
252     sizeof(pw::UniquePtr<int>) == 2 * sizeof(void*),
253     "size_ parameter must be disabled for non-array UniquePtr instances");
254 
255 }  // namespace
256