3 * Copyright (c) 2021 Project CHIP Authors
4 * Copyright 2019 Google Inc. All Rights Reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #include <support/PrivateHeap.h>
20 #include <support/UnitTestRegistration.h>
24 #include <nlunit-test.h>
28 constexpr size_t kBlockHeaderSize = sizeof(internal::PrivateHeapBlockHeader);
30 // Splitting block tests assume we know the size
31 static_assert(kBlockHeaderSize == 16, "Test assumes block size of 16");
33 // helper class for allocating things
34 template <size_t kSize>
35 class PrivateHeapAllocator
38 PrivateHeapAllocator() { PrivateHeapInit(mHeap.buffer, kSize); }
39 void * HeapAlloc(size_t size) { return PrivateHeapAlloc(mHeap.buffer, size); }
40 void HeapFree(void * buffer) { PrivateHeapFree(buffer); }
41 void * HeapRealloc(void * buffer, size_t size) { return PrivateHeapRealloc(mHeap.buffer, buffer, size); }
44 struct alignas(kPrivateHeapAllocationAlignment)
46 uint8_t buffer[kSize];
50 void SingleHeapAllocAndFree(nlTestSuite * inSuite, void * inContext)
52 PrivateHeapAllocator<16 + 2 * kBlockHeaderSize> allocator;
54 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(17)); // insufficient size
55 void * ptr = allocator.HeapAlloc(16);
56 NL_TEST_ASSERT(inSuite, nullptr != ptr);
57 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(1)); // insufficient size
58 memset(ptr, 0xab, 16);
59 allocator.HeapFree(ptr);
61 // allocate different sizes on this heap, see how that goes
62 for (size_t i = 1; i < 17; ++i)
64 ptr = allocator.HeapAlloc(i);
65 NL_TEST_ASSERT(inSuite, nullptr != ptr);
66 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(17 - i)); // insufficient size
67 allocator.HeapFree(ptr);
71 void SplitHeapAllocAndFree(nlTestSuite * inSuite, void * inContext)
73 PrivateHeapAllocator<128> allocator;
75 // <HDR-FREE> 96 <HDR-END>
77 void * p1 = allocator.HeapAlloc(30);
78 NL_TEST_ASSERT(inSuite, nullptr != p1);
80 // <HDR-IN_USE> 32 <HRD-FREE> 48 <HDR-END>
82 void * p2 = allocator.HeapAlloc(4);
83 NL_TEST_ASSERT(inSuite, nullptr != p2);
85 // <HDR-IN_USE> 32 <HRD-IN_USE> 8 <HDR-FREE> 24 <HDR-END>
87 allocator.HeapFree(p1);
89 // <HDR-FREE> 32 <HRD-IN_USE> 8 <HDR-FREE> 24 <HDR-END>
91 allocator.HeapFree(p2);
93 // <HDR-FREE> 96 <HDR-END>
95 p1 = allocator.HeapAlloc(90);
96 NL_TEST_ASSERT(inSuite, nullptr != p1);
97 allocator.HeapFree(p1);
100 void FreeMergeNext(nlTestSuite * inSuite, void * inContext)
102 PrivateHeapAllocator<5 * 16> allocator;
104 void * p1 = allocator.HeapAlloc(16);
105 void * p2 = allocator.HeapAlloc(16);
107 NL_TEST_ASSERT(inSuite, nullptr != p1);
108 NL_TEST_ASSERT(inSuite, nullptr != p2);
109 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(1));
111 memset(p1, 0xab, 16);
112 memset(p2, 0xcd, 16);
114 // freeing 1,2 should clear space
115 allocator.HeapFree(p1);
116 allocator.HeapFree(p2);
118 p1 = allocator.HeapAlloc(3 * 16);
119 NL_TEST_ASSERT(inSuite, nullptr != p1);
120 allocator.HeapFree(p1);
123 void FreeMergePrevious(nlTestSuite * inSuite, void * inContext)
125 PrivateHeapAllocator<5 * 16> allocator;
127 void * p1 = allocator.HeapAlloc(16);
128 void * p2 = allocator.HeapAlloc(16);
130 NL_TEST_ASSERT(inSuite, nullptr != p1);
131 NL_TEST_ASSERT(inSuite, nullptr != p2);
132 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(1));
134 memset(p1, 0xab, 16);
135 memset(p2, 0xcd, 16);
137 // freeing 2,1 should clear space
138 allocator.HeapFree(p2);
139 allocator.HeapFree(p1);
140 p1 = allocator.HeapAlloc(3 * 16);
141 NL_TEST_ASSERT(inSuite, nullptr != p1);
142 allocator.HeapFree(p1);
145 void FreeMergePreviousAndNext(nlTestSuite * inSuite, void * inContext)
148 PrivateHeapAllocator<7 * 16> allocator;
150 void * p1 = allocator.HeapAlloc(16);
151 void * p2 = allocator.HeapAlloc(16);
152 void * p3 = allocator.HeapAlloc(16);
154 NL_TEST_ASSERT(inSuite, nullptr != p1);
155 NL_TEST_ASSERT(inSuite, nullptr != p2);
156 NL_TEST_ASSERT(inSuite, nullptr != p3);
157 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(1));
159 memset(p1, 0xab, 16);
160 memset(p2, 0xcd, 16);
161 memset(p3, 0xef, 16);
163 allocator.HeapFree(p1);
164 allocator.HeapFree(p3);
165 // we have 2 slots of size 16 available now
166 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(17));
168 // Freeing p2 makes enoug space
169 allocator.HeapFree(p2);
170 p1 = allocator.HeapAlloc(5 * 16);
171 NL_TEST_ASSERT(inSuite, nullptr != p1);
172 allocator.HeapFree(p1);
175 void MultipleMerge(nlTestSuite * inSuite, void * inContext)
177 PrivateHeapAllocator<32 * kBlockHeaderSize> allocator;
179 // 31 blocks available for alloc
180 void * p1 = allocator.HeapAlloc(2 * kBlockHeaderSize); // uses up 3 blocks
181 void * p2 = allocator.HeapAlloc(5 * kBlockHeaderSize); // uses up 6 blocks
182 void * p3 = allocator.HeapAlloc(8 * kBlockHeaderSize); // uses up 9 blocks
183 void * p4 = allocator.HeapAlloc(1 * kBlockHeaderSize); // uses up 2 blocks
184 void * p5 = allocator.HeapAlloc(7 * kBlockHeaderSize); // uses up 8 blocks
185 void * p6 = allocator.HeapAlloc(2 * kBlockHeaderSize); // uses up 2 (last given)
187 NL_TEST_ASSERT(inSuite, nullptr != p1);
188 NL_TEST_ASSERT(inSuite, nullptr != p2);
189 NL_TEST_ASSERT(inSuite, nullptr != p3);
190 NL_TEST_ASSERT(inSuite, nullptr != p4);
191 NL_TEST_ASSERT(inSuite, nullptr != p5);
192 NL_TEST_ASSERT(inSuite, nullptr != p6);
194 allocator.HeapFree(p3);
195 allocator.HeapFree(p4);
196 // 10 blocks available (9 from p3 without HDR and 2 from p4 + HDR)
197 p3 = allocator.HeapAlloc(10 * kBlockHeaderSize);
198 NL_TEST_ASSERT(inSuite, nullptr != p3);
199 NL_TEST_ASSERT(inSuite, nullptr == allocator.HeapAlloc(1)); // full
201 allocator.HeapFree(p6);
202 allocator.HeapFree(p5);
203 allocator.HeapFree(p3);
204 allocator.HeapFree(p2);
205 allocator.HeapFree(p1);
207 p1 = allocator.HeapAlloc(30 * kBlockHeaderSize);
208 NL_TEST_ASSERT(inSuite, nullptr != p1);
209 allocator.HeapFree(p1);
212 void ForwardFreeAndRealloc(nlTestSuite * inSuite, void * inContext)
214 constexpr int kNumBlocks = 16;
215 PrivateHeapAllocator<(2 * kNumBlocks + 1) * kBlockHeaderSize> allocator;
216 void * ptrs[kNumBlocks];
218 for (int i = 0; i < kNumBlocks; ++i)
220 ptrs[i] = allocator.HeapAlloc(kBlockHeaderSize);
221 NL_TEST_ASSERT(inSuite, nullptr != ptrs[i]);
222 memset(ptrs[i], 0xab, kBlockHeaderSize);
226 /// |HDR| 16 |HDR| 16 |HDR| ..... |HDR| 16 |HDR|
228 // free each block from the start and re-allocate into a bigger block
229 for (size_t i = 1; i < kNumBlocks; ++i)
231 allocator.HeapFree(ptrs[0]);
232 allocator.HeapFree(ptrs[i]);
234 ptrs[0] = allocator.HeapAlloc((1 + 2 * i) * kBlockHeaderSize);
235 NL_TEST_ASSERT(inSuite, nullptr != ptrs[0]);
237 allocator.HeapFree(ptrs[0]);
240 void BackwardFreeAndRealloc(nlTestSuite * inSuite, void * inContext)
242 constexpr int kNumBlocks = 16;
243 PrivateHeapAllocator<(2 * kNumBlocks + 1) * kBlockHeaderSize> allocator;
244 void * ptrs[kNumBlocks];
246 for (int i = 0; i < kNumBlocks; ++i)
248 ptrs[i] = allocator.HeapAlloc(kBlockHeaderSize);
249 NL_TEST_ASSERT(inSuite, nullptr != ptrs[i]);
250 memset(ptrs[i], 0xab, kBlockHeaderSize);
254 /// |HDR| 16 |HDR| 16 |HDR| ..... |HDR| 16 |HDR|
256 // free each block from the send and re-allocate into a bigger block
257 for (size_t i = 1; i < kNumBlocks; ++i)
259 allocator.HeapFree(ptrs[kNumBlocks - 1]);
260 allocator.HeapFree(ptrs[kNumBlocks - i - 1]);
262 ptrs[kNumBlocks - 1] = allocator.HeapAlloc((1 + 2 * i) * kBlockHeaderSize);
263 NL_TEST_ASSERT(inSuite, nullptr != ptrs[kNumBlocks - 1]);
265 allocator.HeapFree(ptrs[kNumBlocks - 1]);
268 // Fills the data with a known pattern
269 void FillKnownPattern(void * buffer, size_t size, uint8_t start)
271 uint8_t * p = static_cast<uint8_t *>(buffer);
275 uint8_t value = static_cast<uint8_t>(cnt * 31 + 7);
280 // checks if the specified buffer has the given pattern in it
281 bool IsKnownPattern(void * buffer, size_t size, uint8_t start)
283 uint8_t * p = static_cast<uint8_t *>(buffer);
287 uint8_t value = static_cast<uint8_t>(cnt * 31 + 7);
296 void Realloc(nlTestSuite * inSuite, void * inContext)
298 PrivateHeapAllocator<6 * 16> allocator;
300 void * p1 = allocator.HeapRealloc(nullptr, 16); // malloc basically
301 NL_TEST_ASSERT(inSuite, p1 != nullptr);
303 FillKnownPattern(p1, 16, 11);
305 void * p2 = allocator.HeapRealloc(p1, 8); // resize, should fit
306 NL_TEST_ASSERT(inSuite, p1 == p2);
307 NL_TEST_ASSERT(inSuite, IsKnownPattern(p1, 8, 11));
309 p2 = allocator.HeapRealloc(p1, 16); // resize, should fit
310 NL_TEST_ASSERT(inSuite, p1 == p2);
311 NL_TEST_ASSERT(inSuite, IsKnownPattern(p2, 8, 11)); // only 8 bytes are guaranteed
313 FillKnownPattern(p1, 16, 33);
314 p2 = allocator.HeapRealloc(p1, 32); // resize, does not fit. This frees p1
315 NL_TEST_ASSERT(inSuite, p2 != nullptr);
316 NL_TEST_ASSERT(inSuite, p2 != p1); // new reallocation occured
317 NL_TEST_ASSERT(inSuite, IsKnownPattern(p2, 16, 33));
319 void * p3 = allocator.HeapAlloc(48); // insufficient heap for this
320 NL_TEST_ASSERT(inSuite, p3 == nullptr);
322 p1 = allocator.HeapRealloc(p2, 16); // reallocation does not change block size
323 NL_TEST_ASSERT(inSuite, p1 == p2);
325 p3 = allocator.HeapAlloc(48); // still insufficient heap for this
326 NL_TEST_ASSERT(inSuite, p3 == nullptr);
328 p2 = allocator.HeapRealloc(p1, 48); // insufficient heap, p1 is NOT freed
329 NL_TEST_ASSERT(inSuite, p2 == nullptr);
331 p2 = allocator.HeapRealloc(p1, 48); // Repeat the test to ensure p1 is not freed
332 NL_TEST_ASSERT(inSuite, p2 == nullptr);
334 allocator.HeapFree(p1);
336 p3 = allocator.HeapAlloc(48); // above free should have made sufficient space
337 NL_TEST_ASSERT(inSuite, p3 != nullptr);
338 allocator.HeapFree(p3);
341 const nlTest sTests[] = {
342 NL_TEST_DEF("SingleHeapAllocAndFree", SingleHeapAllocAndFree), //
343 NL_TEST_DEF("SplitHeapAllocAndFree", SplitHeapAllocAndFree), //
344 NL_TEST_DEF("FreeMergeNext", FreeMergeNext), //
345 NL_TEST_DEF("FreeMergePrevious", FreeMergePrevious), //
346 NL_TEST_DEF("FreeMergePreviousAndNext", FreeMergePreviousAndNext), //
347 NL_TEST_DEF("MultipleMerge", MultipleMerge), //
348 NL_TEST_DEF("ForwardFreeAndRealloc", ForwardFreeAndRealloc), //
349 NL_TEST_DEF("BackwardFreeAndRealloc", BackwardFreeAndRealloc), //
350 NL_TEST_DEF("Realloc", Realloc), //
351 NL_TEST_SENTINEL() //
356 int TestPrivateHeap(void)
358 nlTestSuite theSuite = { "PrivateHeap", sTests, nullptr, nullptr };
359 nlTestRunner(&theSuite, nullptr);
360 return nlTestRunnerStats(&theSuite);
363 CHIP_REGISTER_TEST_SUITE(TestPrivateHeap)