1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // (C) Copyright Ion Gaztanaga 2015-2015. Distributed under the Boost |
4 | // Software License, Version 1.0. (See accompanying file |
5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
6 | // |
7 | // See http://www.boost.org/libs/container for documentation. |
8 | // |
9 | ////////////////////////////////////////////////////////////////////////////// |
10 | |
11 | #include <boost/container/pmr/monotonic_buffer_resource.hpp> |
12 | #include <boost/container/pmr/global_resource.hpp> |
13 | #include <boost/core/lightweight_test.hpp> |
14 | #include "derived_from_memory_resource.hpp" |
15 | #include "memory_resource_logger.hpp" |
16 | |
17 | using namespace boost::container::pmr; |
18 | |
19 | static const std::size_t AllocCount = 32u; |
20 | |
21 | namespace test_block_chain{ |
22 | |
23 | //explicit block_slist(memory_resource &upstream_rsrc) |
24 | void test_constructor() |
25 | { |
26 | memory_resource_logger mrl; |
27 | block_slist bc(mrl); |
28 | //Resource stored |
29 | BOOST_TEST(&bc.upstream_resource() == &mrl); |
30 | //No allocation performed |
31 | BOOST_TEST(mrl.m_info.size() == 0u); |
32 | } |
33 | |
34 | //void *allocate(std::size_t size) |
35 | void test_allocate() |
36 | { |
37 | memory_resource_logger mrl; |
38 | block_slist bc(mrl); |
39 | |
40 | for(unsigned i = 0; i != unsigned(AllocCount); ++i){ |
41 | //Allocate and trace data |
42 | const std::size_t alloc = i+1; |
43 | char *const addr = (char*)bc.allocate(size: alloc); |
44 | //Should have allocated a new entry |
45 | BOOST_TEST(mrl.m_info.size() == (i+1)); |
46 | //Requested size must be bigger to include metadata |
47 | BOOST_TEST(mrl.m_info[i].bytes > alloc); |
48 | BOOST_TEST(mrl.m_info[i].alignment == memory_resource::max_align); |
49 | //Returned address should be between the allocated buffer |
50 | BOOST_TEST(mrl.m_info[i].address < addr); |
51 | BOOST_TEST(addr < (mrl.m_info[i].address + mrl.m_info[i].bytes)); |
52 | //Allocate size should include all requested size |
53 | BOOST_TEST((addr + alloc) <= (mrl.m_info[i].address + mrl.m_info[i].bytes)); |
54 | //Allocation must be max-aligned |
55 | BOOST_TEST((std::size_t(addr) % memory_resource::max_align) == 0); |
56 | } |
57 | } |
58 | |
59 | //void release() BOOST_NOEXCEPT |
60 | void test_release() |
61 | { |
62 | memory_resource_logger mrl; |
63 | block_slist bc(mrl); |
64 | |
65 | //Allocate and trace data |
66 | char *bufs[AllocCount]; |
67 | for(unsigned i = 0; i != unsigned(AllocCount); ++i){ |
68 | bufs[i] = (char*)bc.allocate(size: i+1); |
69 | } |
70 | (void)bufs; |
71 | //Should have allocated a new entry |
72 | BOOST_TEST(mrl.m_info.size() == AllocCount); |
73 | |
74 | //Now release and check all allocations match deallocations |
75 | bc.release(); |
76 | BOOST_TEST(mrl.m_mismatches == 0); |
77 | BOOST_TEST(mrl.m_info.size() == 0u); |
78 | } |
79 | |
80 | //memory_resource* upstream_resource() |
81 | void test_memory_resource() |
82 | { |
83 | derived_from_memory_resource d; |
84 | block_slist bc(d); |
85 | //Resource stored |
86 | BOOST_TEST(&bc.upstream_resource() == &d); |
87 | } |
88 | |
89 | //~block_slist() { this->release(); } |
90 | void test_destructor() |
91 | { |
92 | memory_resource_logger mrl; |
93 | { |
94 | block_slist bc(mrl); |
95 | |
96 | //Allocate and trace data |
97 | char *bufs[AllocCount]; |
98 | for(unsigned i = 0; i != unsigned(AllocCount); ++i){ |
99 | bufs[i] = (char*)bc.allocate(size: i+1); |
100 | } |
101 | (void)bufs; |
102 | //Should have allocated a new entry |
103 | BOOST_TEST(mrl.m_info.size() == AllocCount); |
104 | |
105 | //Destructor should release all memory |
106 | } |
107 | BOOST_TEST(mrl.m_mismatches == 0); |
108 | BOOST_TEST(mrl.m_info.size() == 0u); |
109 | } |
110 | |
111 | } //namespace test_block_chain { |
112 | |
113 | void test_resource_constructor() |
114 | { |
115 | //First constructor, null resource |
116 | { |
117 | memory_resource_logger mrl; |
118 | BOOST_TEST(mrl.m_info.size() == 0u); |
119 | set_default_resource(&mrl); |
120 | monotonic_buffer_resource m; |
121 | //test postconditions |
122 | BOOST_TEST(m.upstream_resource() == get_default_resource()); |
123 | //test it does not allocate any memory |
124 | BOOST_TEST(mrl.m_info.size() == 0u); |
125 | set_default_resource(0); |
126 | } |
127 | //First constructor, non-null resource |
128 | { |
129 | derived_from_memory_resource dmr; |
130 | dmr.reset(); |
131 | monotonic_buffer_resource m(&dmr); |
132 | //test postconditions |
133 | BOOST_TEST(m.upstream_resource() == &dmr); |
134 | BOOST_TEST(m.next_buffer_size() == monotonic_buffer_resource::initial_next_buffer_size); |
135 | BOOST_TEST(m.current_buffer() == 0); |
136 | //test it does not allocate any memory |
137 | BOOST_TEST(dmr.do_allocate_called == false); |
138 | } |
139 | } |
140 | |
141 | void test_initial_size_constructor() |
142 | { |
143 | //Second constructor, null resource |
144 | const std::size_t initial_size = monotonic_buffer_resource::initial_next_buffer_size*2; |
145 | { |
146 | memory_resource_logger mrl; |
147 | BOOST_TEST(mrl.m_info.size() == 0u); |
148 | set_default_resource(&mrl); |
149 | monotonic_buffer_resource m(initial_size); |
150 | //test postconditions |
151 | BOOST_TEST(m.upstream_resource() == get_default_resource()); |
152 | BOOST_TEST(m.next_buffer_size() >= initial_size); |
153 | BOOST_TEST(m.current_buffer() == 0); |
154 | //test it does not allocate any memory |
155 | BOOST_TEST(mrl.m_info.size() == 0u); |
156 | set_default_resource(0); |
157 | } |
158 | //Second constructor, non-null resource |
159 | { |
160 | derived_from_memory_resource dmr; |
161 | dmr.reset(); |
162 | monotonic_buffer_resource m(initial_size, &dmr); |
163 | //test postconditions |
164 | BOOST_TEST(m.upstream_resource() == &dmr); |
165 | BOOST_TEST(m.next_buffer_size() >= initial_size); |
166 | BOOST_TEST(m.current_buffer() == 0); |
167 | //test it does not allocate any memory |
168 | BOOST_TEST(dmr.do_allocate_called == false); |
169 | } |
170 | } |
171 | |
172 | void test_buffer_constructor() |
173 | { |
174 | const std::size_t BufSz = monotonic_buffer_resource::initial_next_buffer_size*2; |
175 | unsigned char buf[BufSz]; |
176 | //Third constructor, null resource |
177 | { |
178 | memory_resource_logger mrl; |
179 | BOOST_TEST(mrl.m_info.size() == 0u); |
180 | set_default_resource(&mrl); |
181 | monotonic_buffer_resource m(buf, BufSz); |
182 | //test postconditions |
183 | BOOST_TEST(m.upstream_resource() == get_default_resource()); |
184 | BOOST_TEST(m.next_buffer_size() >= BufSz*2); |
185 | BOOST_TEST(m.current_buffer() == buf); |
186 | //test it does not allocate any memory |
187 | BOOST_TEST(mrl.m_info.size() == 0u); |
188 | set_default_resource(0); |
189 | } |
190 | //Third constructor, non-null resource |
191 | { |
192 | derived_from_memory_resource dmr; |
193 | dmr.reset(); |
194 | monotonic_buffer_resource m(buf, sizeof(buf), &dmr); |
195 | //test postconditions |
196 | BOOST_TEST(m.upstream_resource() == &dmr); |
197 | BOOST_TEST(m.next_buffer_size() >= sizeof(buf)*2); |
198 | BOOST_TEST(m.current_buffer() == buf); |
199 | //test it does not allocate any memory |
200 | BOOST_TEST(dmr.do_allocate_called == false); |
201 | } |
202 | //Check for empty buffers |
203 | { |
204 | monotonic_buffer_resource m(buf, 0); |
205 | BOOST_TEST(m.upstream_resource() == get_default_resource()); |
206 | BOOST_TEST(m.next_buffer_size() > 1); |
207 | BOOST_TEST(m.current_buffer() == buf); |
208 | } |
209 | } |
210 | |
211 | struct derived_from_monotonic_buffer_resource |
212 | : public monotonic_buffer_resource |
213 | { |
214 | explicit derived_from_monotonic_buffer_resource(memory_resource *p) |
215 | : monotonic_buffer_resource(p) |
216 | {} |
217 | |
218 | explicit derived_from_monotonic_buffer_resource(std::size_t initial_size, memory_resource* upstream) |
219 | : monotonic_buffer_resource(initial_size, upstream) |
220 | {} |
221 | |
222 | explicit derived_from_monotonic_buffer_resource(void* buffer, std::size_t buffer_size, memory_resource* upstream) |
223 | : monotonic_buffer_resource(buffer, buffer_size, upstream) |
224 | {} |
225 | |
226 | using monotonic_buffer_resource::do_allocate; |
227 | using monotonic_buffer_resource::do_deallocate; |
228 | using monotonic_buffer_resource::do_is_equal; |
229 | }; |
230 | |
231 | void test_upstream_resource() |
232 | { |
233 | //Allocate buffer first to avoid stack-use-after-scope in monotonic_buffer_resource's destructor |
234 | const std::size_t BufSz = monotonic_buffer_resource::initial_next_buffer_size; |
235 | boost::move_detail::aligned_storage<BufSz+block_slist::header_size>::type buf; |
236 | //Test stores the resource and uses it to allocate memory |
237 | derived_from_memory_resource dmr; |
238 | dmr.reset(); |
239 | derived_from_monotonic_buffer_resource dmbr(&dmr); |
240 | //Resource must be stored and initial values given (no current buffer) |
241 | BOOST_TEST(dmbr.upstream_resource() == &dmr); |
242 | BOOST_TEST(dmbr.next_buffer_size() == monotonic_buffer_resource::initial_next_buffer_size); |
243 | BOOST_TEST(dmbr.current_buffer() == 0); |
244 | //Test it does not allocate any memory |
245 | BOOST_TEST(dmr.do_allocate_called == false); |
246 | //Now stub buffer storage it as the return buffer |
247 | //for "derived_from_memory_resource": |
248 | dmr.do_allocate_return = &buf; |
249 | //Test that allocation uses the upstream_resource() |
250 | void *addr = dmbr.do_allocate(bytes: 1u, alignment: 1u); |
251 | //Test returns stubbed memory with the internal initial size plus metadata size |
252 | BOOST_TEST(addr > (char*)&buf); |
253 | BOOST_TEST(addr < (char*)(&buf+1)); |
254 | BOOST_TEST(dmr.do_allocate_called == true); |
255 | BOOST_TEST(dmr.do_allocate_bytes > BufSz); |
256 | //Alignment for the resource must be max_align |
257 | BOOST_TEST(dmr.do_allocate_alignment == memory_resource::max_align); |
258 | } |
259 | |
260 | void test_do_allocate() |
261 | { |
262 | memory_resource_logger mrl; |
263 | { |
264 | std::size_t remaining_storage = 0u; |
265 | derived_from_monotonic_buffer_resource dmbr(&mrl); |
266 | //First test, no buffer |
267 | { |
268 | dmbr.do_allocate(bytes: 1, alignment: 1); |
269 | //It should allocate initial size |
270 | BOOST_TEST(mrl.m_info.size() == 1u); |
271 | //... which requests the initial size plus the header size to the allcoator |
272 | BOOST_TEST(mrl.m_info[0].bytes == monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size); |
273 | std::size_t remaining = dmbr.remaining_storage(alignment: 1u); |
274 | //Remaining storage should be one less than initial, as we requested 1 byte with minimal alignment |
275 | BOOST_TEST(remaining == monotonic_buffer_resource::initial_next_buffer_size-1u); |
276 | remaining_storage = remaining; |
277 | } |
278 | //Now ask for more internal storage with misaligned current buffer |
279 | { |
280 | //Test wasted space |
281 | std::size_t wasted_due_to_alignment; |
282 | dmbr.remaining_storage(alignment: 4u, wasted_due_to_alignment); |
283 | BOOST_TEST(wasted_due_to_alignment == 3u); |
284 | dmbr.do_allocate(bytes: 4, alignment: 4); |
285 | //It should not have allocated |
286 | BOOST_TEST(mrl.m_info.size() == 1u); |
287 | std::size_t remaining = dmbr.remaining_storage(alignment: 1u); |
288 | //We wasted some bytes due to alignment plus 4 bytes of real storage |
289 | BOOST_TEST(remaining == remaining_storage - 4 - wasted_due_to_alignment); |
290 | remaining_storage = remaining; |
291 | } |
292 | //Now request the same alignment to test no storage is wasted |
293 | { |
294 | std::size_t wasted_due_to_alignment; |
295 | std::size_t remaining = dmbr.remaining_storage(alignment: 1u, wasted_due_to_alignment); |
296 | BOOST_TEST(mrl.m_info.size() == 1u); |
297 | dmbr.do_allocate(bytes: 4, alignment: 4); |
298 | //It should not have allocated |
299 | BOOST_TEST(mrl.m_info.size() == 1u); |
300 | remaining = dmbr.remaining_storage(alignment: 1u); |
301 | //We wasted no bytes due to alignment plus 4 bytes of real storage |
302 | BOOST_TEST(remaining == remaining_storage - 4u); |
303 | remaining_storage = remaining; |
304 | } |
305 | //Now exhaust the remaining storage with 2 byte alignment (the last allocation |
306 | //was 4 bytes with 4 byte alignment) so it should be already 2-byte aligned. |
307 | { |
308 | dmbr.do_allocate(bytes: remaining_storage, alignment: 2); |
309 | std::size_t wasted_due_to_alignment; |
310 | std::size_t remaining = dmbr.remaining_storage(alignment: 1u, wasted_due_to_alignment); |
311 | BOOST_TEST(wasted_due_to_alignment == 0u); |
312 | BOOST_TEST(remaining == 0u); |
313 | //It should not have allocated |
314 | BOOST_TEST(mrl.m_info.size() == 1u); |
315 | remaining_storage = 0u; |
316 | } |
317 | //The next allocation should trigger the upstream resource, even with a 1 byte |
318 | //allocation. |
319 | { |
320 | dmbr.do_allocate(bytes: 1u, alignment: 1u); |
321 | BOOST_TEST(mrl.m_info.size() == 2u); |
322 | //The next allocation should be geometrically bigger. |
323 | BOOST_TEST(mrl.m_info[1].bytes == 2*monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size); |
324 | std::size_t wasted_due_to_alignment; |
325 | //For a 2 byte alignment one byte will be wasted from the previous 1 byte allocation |
326 | std::size_t remaining = dmbr.remaining_storage(alignment: 2u, wasted_due_to_alignment); |
327 | BOOST_TEST(wasted_due_to_alignment == 1u); |
328 | BOOST_TEST(remaining == (mrl.m_info[1].bytes - 1u - wasted_due_to_alignment - block_slist::header_size)); |
329 | //It should not have allocated |
330 | remaining_storage = dmbr.remaining_storage(alignment: 1u); |
331 | } |
332 | //Now try a bigger than next allocation and see if next_buffer_size is doubled. |
333 | { |
334 | std::size_t next_alloc = 5*monotonic_buffer_resource::initial_next_buffer_size; |
335 | dmbr.do_allocate(bytes: next_alloc, alignment: 1u); |
336 | BOOST_TEST(mrl.m_info.size() == 3u); |
337 | //The next allocation should be geometrically bigger. |
338 | BOOST_TEST(mrl.m_info[2].bytes == 8*monotonic_buffer_resource::initial_next_buffer_size+block_slist::header_size); |
339 | remaining_storage = dmbr.remaining_storage(alignment: 1u); |
340 | } |
341 | } |
342 | //derived_from_monotonic_buffer_resource dmbr(&mrl) is destroyed |
343 | BOOST_TEST(mrl.m_mismatches == 0u); |
344 | BOOST_TEST(mrl.m_info.size() == 0u); |
345 | |
346 | //Now use a local buffer |
347 | { |
348 | boost::move_detail::aligned_storage |
349 | <monotonic_buffer_resource::initial_next_buffer_size>::type buf; |
350 | //Supply an external buffer |
351 | derived_from_monotonic_buffer_resource dmbr(&buf, sizeof(buf), &mrl); |
352 | BOOST_TEST(dmbr.remaining_storage(1u) == sizeof(buf)); |
353 | //Allocate all remaining storage |
354 | dmbr.do_allocate(bytes: dmbr.remaining_storage(alignment: 1u), alignment: 1u); |
355 | //No new allocation should have occurred |
356 | BOOST_TEST(mrl.m_info.size() == 0u); |
357 | BOOST_TEST(dmbr.remaining_storage(1u) == 0u); |
358 | } |
359 | BOOST_TEST(mrl.m_mismatches == 0u); |
360 | BOOST_TEST(mrl.m_info.size() == 0u); |
361 | } |
362 | |
363 | void test_do_deallocate() |
364 | { |
365 | memory_resource_logger mrl; |
366 | const std::size_t initial_size = 1u; |
367 | { |
368 | derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl); |
369 | //First test, no buffer |
370 | const unsigned iterations = 8; |
371 | char *bufs[iterations]; |
372 | std::size_t sizes[iterations]; |
373 | //Test each iteration allocates memory |
374 | for(unsigned i = 0; i != iterations; ++i) |
375 | { |
376 | sizes[i] = dmbr.remaining_storage()+1; |
377 | bufs[i] = (char*)dmbr.do_allocate(bytes: sizes[i], alignment: 1); |
378 | BOOST_TEST(mrl.m_info.size() == (i+1)); |
379 | } |
380 | std::size_t remaining = dmbr.remaining_storage(); |
381 | //Test do_deallocate does not release any storage |
382 | for(unsigned i = 0; i != iterations; ++i) |
383 | { |
384 | dmbr.do_deallocate(p: bufs[i], bytes: sizes[i], alignment: 1u); |
385 | BOOST_TEST(mrl.m_info.size() == iterations); |
386 | BOOST_TEST(remaining == dmbr.remaining_storage()); |
387 | BOOST_TEST(mrl.m_mismatches == 0u); |
388 | } |
389 | } |
390 | } |
391 | |
392 | void test_do_is_equal() |
393 | { |
394 | //! <b>Returns</b>: |
395 | //! `this == dynamic_cast<const monotonic_buffer_resource*>(&other)`. |
396 | memory_resource_logger mrl; |
397 | derived_from_monotonic_buffer_resource dmbr(&mrl); |
398 | derived_from_monotonic_buffer_resource dmbr2(&mrl); |
399 | BOOST_TEST(true == dmbr.do_is_equal(dmbr)); |
400 | BOOST_TEST(false == dmbr.do_is_equal(dmbr2)); |
401 | //A different type should be always different |
402 | derived_from_memory_resource dmr; |
403 | BOOST_TEST(false == dmbr.do_is_equal(dmr)); |
404 | } |
405 | |
406 | void test_release() |
407 | { |
408 | { |
409 | memory_resource_logger mrl; |
410 | const std::size_t initial_size = 1u; |
411 | derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl); |
412 | //First test, no buffer |
413 | const unsigned iterations = 8; |
414 | //Test each iteration allocates memory |
415 | for(unsigned i = 0; i != iterations; ++i) |
416 | { |
417 | dmbr.do_allocate(bytes: dmbr.remaining_storage()+1, alignment: 1); |
418 | BOOST_TEST(mrl.m_info.size() == (i+1)); |
419 | } |
420 | //Release and check memory was released |
421 | dmbr.release(); |
422 | BOOST_TEST(mrl.m_mismatches == 0u); |
423 | BOOST_TEST(mrl.m_info.size() == 0u); |
424 | } |
425 | //Now use a local buffer |
426 | { |
427 | boost::move_detail::aligned_storage |
428 | <monotonic_buffer_resource::initial_next_buffer_size>::type buf; |
429 | //Supply an external buffer |
430 | monotonic_buffer_resource monr(&buf, sizeof(buf)); |
431 | memory_resource &mr = monr; |
432 | BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf)); |
433 | //Allocate all remaining storage |
434 | mr.allocate(bytes: monr.remaining_storage(alignment: 1u), alignment: 1u); |
435 | BOOST_TEST(monr.current_buffer() == ((char*)&buf + sizeof(buf))); |
436 | //No new allocation should have occurred |
437 | BOOST_TEST(monr.remaining_storage(1u) == 0u); |
438 | //Release and check memory was released and the original buffer is back |
439 | monr.release(); |
440 | BOOST_TEST(monr.remaining_storage(1u) == sizeof(buf)); |
441 | BOOST_TEST(monr.current_buffer() == &buf); |
442 | } |
443 | } |
444 | |
445 | void test_destructor() |
446 | { |
447 | memory_resource_logger mrl; |
448 | const std::size_t initial_size = 1u; |
449 | { |
450 | derived_from_monotonic_buffer_resource dmbr(initial_size, &mrl); |
451 | //First test, no buffer |
452 | const unsigned iterations = 8; |
453 | //Test each iteration allocates memory |
454 | for(unsigned i = 0; i != iterations; ++i) |
455 | { |
456 | dmbr.do_allocate(bytes: dmbr.remaining_storage()+1, alignment: 1); |
457 | BOOST_TEST(mrl.m_info.size() == (i+1)); |
458 | } |
459 | } //dmbr is destroyed, memory should be released |
460 | BOOST_TEST(mrl.m_mismatches == 0u); |
461 | BOOST_TEST(mrl.m_info.size() == 0u); |
462 | } |
463 | |
464 | int main() |
465 | { |
466 | test_block_chain::test_constructor(); |
467 | test_block_chain::test_allocate(); |
468 | test_block_chain::test_release(); |
469 | test_block_chain::test_memory_resource(); |
470 | test_block_chain::test_destructor(); |
471 | |
472 | test_resource_constructor(); |
473 | test_initial_size_constructor(); |
474 | test_buffer_constructor(); |
475 | |
476 | test_upstream_resource(); |
477 | test_do_allocate(); |
478 | test_do_deallocate(); |
479 | test_do_is_equal(); |
480 | test_release(); |
481 | test_destructor(); |
482 | return ::boost::report_errors(); |
483 | } |
484 | |