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
17using namespace boost::container::pmr;
18
19static const std::size_t AllocCount = 32u;
20
21namespace test_block_chain{
22
23//explicit block_slist(memory_resource &upstream_rsrc)
24void 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)
35void 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
60void 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()
81void 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(); }
90void 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
113void 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
141void 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
172void 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
211struct 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
231void 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
260void 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
363void 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
392void 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
406void 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
445void 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
464int 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

source code of boost/libs/container/test/monotonic_buffer_resource_test.cpp