blob: f4267b35d5ea515633a695a57d00db6f0ff76cbb [file] [log] [blame]
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +00001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdalign.h>
18
19#include <gmock/gmock.h>
20
21extern "C" {
22#include "hf/mpool.h"
23}
24
25namespace
26{
27using ::testing::IsNull;
28using ::testing::NotNull;
29
30/**
31 * Checks that the given allocations come from the given chunks.
32 */
33bool check_allocs(std::vector<std::unique_ptr<char[]>>& chunks,
34 std::vector<uintptr_t>& allocs, size_t entries_per_chunk,
35 size_t entry_size)
36{
37 size_t i, j;
38
39 if (allocs.size() != chunks.size() * entries_per_chunk) {
40 return false;
41 }
42
43 sort(allocs.begin(), allocs.end());
44 sort(chunks.begin(), chunks.end(),
45 [](const std::unique_ptr<char[]>& a,
46 const std::unique_ptr<char[]>& b) {
47 return a.get() < b.get();
48 });
49
50 for (i = 0; i < chunks.size(); i++) {
51 if ((uintptr_t)chunks[i].get() !=
52 allocs[i * entries_per_chunk]) {
53 return false;
54 }
55
56 for (j = 1; j < entries_per_chunk; j++) {
57 size_t k = i * entries_per_chunk + j;
58 if (allocs[k] != allocs[k - 1] + entry_size) {
59 return false;
60 }
61 }
62 }
63
64 return true;
65}
66
67/**
68 * Add chunks to the given mem pool and chunk vector.
69 */
70static void add_chunks(std::vector<std::unique_ptr<char[]>>& chunks,
71 struct mpool* p, size_t count, size_t size)
72{
73 size_t i;
74
75 for (i = 0; i < count; i++) {
76 chunks.emplace_back(std::make_unique<char[]>(size));
77 mpool_add_chunk(p, chunks.back().get(), size);
78 }
79}
80
81/**
82 * Validates allocations from a memory pool.
83 */
84TEST(mpool, allocation)
85{
86 struct mpool p;
87 constexpr size_t entry_size = 16;
88 constexpr size_t entries_per_chunk = 10;
89 constexpr size_t chunk_count = 10;
90 std::vector<std::unique_ptr<char[]>> chunks;
91 std::vector<uintptr_t> allocs;
92 void* ret;
93
94 mpool_init(&p, entry_size);
95
96 /* Allocate from an empty pool. */
97 EXPECT_THAT(mpool_alloc(&p), IsNull());
98
99 /*
100 * Add a chunk that is too small, it should be ignored, and allocation
101 * should return NULL.
102 */
103 mpool_add_chunk(&p, NULL, entry_size - 1);
104 EXPECT_THAT(mpool_alloc(&p), IsNull());
105
106 /* Allocate a number of chunks and add them to the pool. */
107 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
108
109 /* Allocate from the pool until we run out of memory. */
110 while ((ret = mpool_alloc(&p))) {
111 allocs.push_back((uintptr_t)ret);
112 }
113
114 /* Check that returned entries are within chunks that were added. */
115 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
116 true);
117}
118
119/**
120 * Validates frees into a memory pool.
121 */
122TEST(mpool, freeing)
123{
124 struct mpool p;
125 constexpr size_t entry_size = 16;
126 constexpr size_t entries_per_chunk = 10;
127 constexpr size_t chunk_count = 10;
128 std::vector<std::unique_ptr<char[]>> chunks;
129 std::vector<uintptr_t> allocs;
130 size_t i;
131 alignas(entry_size) char entry[entry_size];
132 void* ret;
133
134 mpool_init(&p, entry_size);
135
136 /* Allocate from an empty pool. */
137 EXPECT_THAT(mpool_alloc(&p), IsNull());
138
139 /* Free an entry into the pool, then allocate it back. */
140 mpool_free(&p, &entry[0]);
141 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
142 EXPECT_THAT(mpool_alloc(&p), IsNull());
143
144 /* Allocate a number of chunks and add them to the pool. */
145 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
146
147 /*
148 * Free again into the pool. Ensure that we get entry back on next
149 * allocation instead of something from the chunks.
150 */
151 mpool_free(&p, &entry[0]);
152 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
153
154 /* Allocate from the pool until we run out of memory. */
155 while ((ret = mpool_alloc(&p))) {
156 allocs.push_back((uintptr_t)ret);
157 }
158
159 /*
160 * Free again into the pool. Ensure that we get entry back on next
161 * allocation instead of something from the chunks.
162 */
163 mpool_free(&p, &entry[0]);
164 EXPECT_THAT(mpool_alloc(&p), (void*)&entry[0]);
165
166 /* Add entries back to the pool by freeing them. */
167 for (i = 0; i < allocs.size(); i++) {
168 mpool_free(&p, (void*)allocs[i]);
169 }
170 allocs.clear();
171
172 /* Allocate from the pool until we run out of memory. */
173 while ((ret = mpool_alloc(&p))) {
174 allocs.push_back((uintptr_t)ret);
175 }
176
177 /* Check that returned entries are within chunks that were added. */
178 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
179 true);
180}
181
182/**
183 * Initialises a memory pool from an existing one.
184 */
185TEST(mpool, init_from)
186{
187 struct mpool p, q;
188 constexpr size_t entry_size = 16;
189 constexpr size_t entries_per_chunk = 10;
190 constexpr size_t chunk_count = 10;
191 std::vector<std::unique_ptr<char[]>> chunks;
192 std::vector<uintptr_t> allocs;
193 size_t i;
194 void* ret;
195
196 mpool_init(&p, entry_size);
197
198 /* Allocate a number of chunks and add them to the pool. */
199 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
200
201 /* Allocate half of the elements. */
202 for (i = 0; i < entries_per_chunk * chunk_count / 2; i++) {
203 void* ret = mpool_alloc(&p);
204 ASSERT_THAT(ret, NotNull());
205 allocs.push_back((uintptr_t)ret);
206 }
207
208 /* Add entries back to the pool by freeing them. */
209 for (i = 0; i < allocs.size(); i++) {
210 mpool_free(&p, (void*)allocs[i]);
211 }
212 allocs.clear();
213
214 /* Initialise q from p. */
215 mpool_init_from(&q, &p);
216
217 /* Allocation from p must now fail. */
218 EXPECT_THAT(mpool_alloc(&p), IsNull());
219
220 /* Allocate from q until we run out of memory. */
221 while ((ret = mpool_alloc(&q))) {
222 allocs.push_back((uintptr_t)ret);
223 }
224
225 /* Check that returned entries are within chunks that were added. */
226 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
227 true);
228}
229
230/**
231 * Initialises a memory pool from an existing one.
232 */
233TEST(mpool, alloc_contiguous)
234{
235 struct mpool p;
236 constexpr size_t entry_size = 16;
Andrew Scullf0f6be52018-12-21 14:44:33 +0000237 constexpr size_t entries_per_chunk = 12;
Wedson Almeida Filho11a9b0b2018-11-30 18:21:51 +0000238 constexpr size_t chunk_count = 10;
239 std::vector<std::unique_ptr<char[]>> chunks;
240 std::vector<uintptr_t> allocs;
241 size_t i;
242 void* ret;
243 uintptr_t next;
244
245 mpool_init(&p, entry_size);
246
247 /* Allocate a number of chunks and add them to the pool. */
248 add_chunks(chunks, &p, chunk_count, entries_per_chunk * entry_size);
249
250 /*
251 * Allocate entries until the remaining chunk is aligned to 2 entries,
252 * but not aligned to 4 entries.
253 */
254 do {
255 ret = mpool_alloc(&p);
256 ASSERT_THAT(ret, NotNull());
257 allocs.push_back((uintptr_t)ret);
258 next = (uintptr_t)ret / entry_size + 1;
259 } while ((next % 4) != 2);
260
261 /* Allocate 5 entries with an alignment of 4. So two must be skipped. */
262 ret = mpool_alloc_contiguous(&p, 5, 4);
263 ASSERT_THAT(ret, NotNull());
264 ASSERT_THAT((uintptr_t)ret, (next + 2) * entry_size);
265 for (i = 0; i < 5; i++) {
266 allocs.push_back((uintptr_t)ret + i * entry_size);
267 }
268
269 /* Allocate a whole chunk. */
270 ret = mpool_alloc_contiguous(&p, entries_per_chunk, 1);
271 ASSERT_THAT(ret, NotNull());
272 for (i = 0; i < entries_per_chunk; i++) {
273 allocs.push_back((uintptr_t)ret + i * entry_size);
274 }
275
276 /* Allocate 2 entries that are already aligned. */
277 ret = mpool_alloc_contiguous(&p, 2, 1);
278 ASSERT_THAT(ret, NotNull());
279 allocs.push_back((uintptr_t)ret);
280 allocs.push_back((uintptr_t)ret + entry_size);
281
282 /* Allocate from p until we run out of memory. */
283 while ((ret = mpool_alloc(&p))) {
284 allocs.push_back((uintptr_t)ret);
285 }
286
287 /* Check that returned entries are within chunks that were added. */
288 ASSERT_THAT(check_allocs(chunks, allocs, entries_per_chunk, entry_size),
289 true);
290}
291
292} /* namespace */