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