1 //////////////////////////////////////////////////////////////////////////////
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)
7 // See http://www.boost.org/libs/container for documentation.
9 //////////////////////////////////////////////////////////////////////////////
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"
17 using namespace boost::container::pmr
;
19 static const std::size_t AllocCount
= 32u;
21 namespace test_block_chain
{
23 //explicit block_slist(memory_resource &upstream_rsrc)
24 void test_constructor()
26 memory_resource_logger mrl
;
29 BOOST_TEST(&bc
.upstream_resource() == &mrl
);
30 //No allocation performed
31 BOOST_TEST(mrl
.m_info
.size() == 0u);
34 //void *allocate(std::size_t size)
37 memory_resource_logger mrl
;
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(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);
59 //void release() BOOST_NOEXCEPT
62 memory_resource_logger mrl
;
65 //Allocate and trace data
66 char *bufs
[AllocCount
];
67 for(unsigned i
= 0; i
!= unsigned(AllocCount
); ++i
){
68 bufs
[i
] = (char*)bc
.allocate(i
+1);
71 //Should have allocated a new entry
72 BOOST_TEST(mrl
.m_info
.size() == AllocCount
);
74 //Now release and check all allocations match deallocations
76 BOOST_TEST(mrl
.m_mismatches
== 0);
77 BOOST_TEST(mrl
.m_info
.size() == 0u);
80 //memory_resource* upstream_resource()
81 void test_memory_resource()
83 derived_from_memory_resource d
;
86 BOOST_TEST(&bc
.upstream_resource() == &d
);
89 //~block_slist() { this->release(); }
90 void test_destructor()
92 memory_resource_logger mrl
;
96 //Allocate and trace data
97 char *bufs
[AllocCount
];
98 for(unsigned i
= 0; i
!= unsigned(AllocCount
); ++i
){
99 bufs
[i
] = (char*)bc
.allocate(i
+1);
102 //Should have allocated a new entry
103 BOOST_TEST(mrl
.m_info
.size() == AllocCount
);
105 //Destructor should release all memory
107 BOOST_TEST(mrl
.m_mismatches
== 0);
108 BOOST_TEST(mrl
.m_info
.size() == 0u);
111 } //namespace test_block_chain {
113 void test_resource_constructor()
115 //First constructor, null resource
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);
127 //First constructor, non-null resource
129 derived_from_memory_resource dmr
;
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);
141 void test_initial_size_constructor()
143 //Second constructor, null resource
144 const std::size_t initial_size
= monotonic_buffer_resource::initial_next_buffer_size
*2;
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);
158 //Second constructor, non-null resource
160 derived_from_memory_resource dmr
;
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);
172 void test_buffer_constructor()
174 const std::size_t BufSz
= monotonic_buffer_resource::initial_next_buffer_size
*2;
175 unsigned char buf
[BufSz
];
176 //Third constructor, null resource
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);
190 //Third constructor, non-null resource
192 derived_from_memory_resource dmr
;
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);
202 //Check for empty buffers
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
);
211 struct derived_from_monotonic_buffer_resource
212 : public monotonic_buffer_resource
214 explicit derived_from_monotonic_buffer_resource(memory_resource
*p
)
215 : monotonic_buffer_resource(p
)
218 explicit derived_from_monotonic_buffer_resource(std::size_t initial_size
, memory_resource
* upstream
)
219 : monotonic_buffer_resource(initial_size
, upstream
)
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
)
226 using monotonic_buffer_resource::do_allocate
;
227 using monotonic_buffer_resource::do_deallocate
;
228 using monotonic_buffer_resource::do_is_equal
;
231 void test_upstream_resource()
233 //Test stores the resource and uses it to allocate memory
234 derived_from_memory_resource dmr
;
236 derived_from_monotonic_buffer_resource
dmbr(&dmr
);
237 //Resource must be stored and initial values given (no current buffer)
238 BOOST_TEST(dmbr
.upstream_resource() == &dmr
);
239 BOOST_TEST(dmbr
.next_buffer_size() == monotonic_buffer_resource::initial_next_buffer_size
);
240 BOOST_TEST(dmbr
.current_buffer() == 0);
241 //Test it does not allocate any memory
242 BOOST_TEST(dmr
.do_allocate_called
== false);
243 const std::size_t BufSz
= monotonic_buffer_resource::initial_next_buffer_size
;
244 //Now allocate storage, and stub it as the return buffer
245 //for "derived_from_memory_resource":
246 boost::move_detail::aligned_storage
<BufSz
+block_slist::header_size
>::type buf
;
247 dmr
.do_allocate_return
= &buf
;
248 //Test that allocation uses the upstream_resource()
249 void *addr
= dmbr
.do_allocate(1u, 1u);
250 //Test returns stubbed memory with the internal initial size plus metadata size
251 BOOST_TEST(addr
> (char*)&buf
);
252 BOOST_TEST(addr
< (char*)(&buf
+1));
253 BOOST_TEST(dmr
.do_allocate_called
== true);
254 BOOST_TEST(dmr
.do_allocate_bytes
> BufSz
);
255 //Alignment for the resource must be max_align
256 BOOST_TEST(dmr
.do_allocate_alignment
== memory_resource::max_align
);
259 void test_do_allocate()
261 memory_resource_logger mrl
;
263 std::size_t remaining_storage
= 0u;
264 derived_from_monotonic_buffer_resource
dmbr(&mrl
);
265 //First test, no buffer
267 dmbr
.do_allocate(1, 1);
268 //It should allocate initial size
269 BOOST_TEST(mrl
.m_info
.size() == 1u);
270 //... which requests the initial size plus the header size to the allcoator
271 BOOST_TEST(mrl
.m_info
[0].bytes
== monotonic_buffer_resource::initial_next_buffer_size
+block_slist::header_size
);
272 std::size_t remaining
= dmbr
.remaining_storage(1u);
273 //Remaining storage should be one less than initial, as we requested 1 byte with minimal alignment
274 BOOST_TEST(remaining
== monotonic_buffer_resource::initial_next_buffer_size
-1u);
275 remaining_storage
= remaining
;
277 //Now ask for more internal storage with misaligned current buffer
280 std::size_t wasted_due_to_alignment
;
281 dmbr
.remaining_storage(4u, wasted_due_to_alignment
);
282 BOOST_TEST(wasted_due_to_alignment
== 3u);
283 dmbr
.do_allocate(4, 4);
284 //It should not have allocated
285 BOOST_TEST(mrl
.m_info
.size() == 1u);
286 std::size_t remaining
= dmbr
.remaining_storage(1u);
287 //We wasted some bytes due to alignment plus 4 bytes of real storage
288 BOOST_TEST(remaining
== remaining_storage
- 4 - wasted_due_to_alignment
);
289 remaining_storage
= remaining
;
291 //Now request the same alignment to test no storage is wasted
293 std::size_t wasted_due_to_alignment
;
294 std::size_t remaining
= dmbr
.remaining_storage(1u, wasted_due_to_alignment
);
295 BOOST_TEST(mrl
.m_info
.size() == 1u);
296 dmbr
.do_allocate(4, 4);
297 //It should not have allocated
298 BOOST_TEST(mrl
.m_info
.size() == 1u);
299 remaining
= dmbr
.remaining_storage(1u);
300 //We wasted no bytes due to alignment plus 4 bytes of real storage
301 BOOST_TEST(remaining
== remaining_storage
- 4u);
302 remaining_storage
= remaining
;
304 //Now exhaust the remaining storage with 2 byte alignment (the last allocation
305 //was 4 bytes with 4 byte alignment) so it should be already 2-byte aligned.
307 dmbr
.do_allocate(remaining_storage
, 2);
308 std::size_t wasted_due_to_alignment
;
309 std::size_t remaining
= dmbr
.remaining_storage(1u, wasted_due_to_alignment
);
310 BOOST_TEST(wasted_due_to_alignment
== 0u);
311 BOOST_TEST(remaining
== 0u);
312 //It should not have allocated
313 BOOST_TEST(mrl
.m_info
.size() == 1u);
314 remaining_storage
= 0u;
316 //The next allocation should trigger the upstream resource, even with a 1 byte
319 dmbr
.do_allocate(1u, 1u);
320 BOOST_TEST(mrl
.m_info
.size() == 2u);
321 //The next allocation should be geometrically bigger.
322 BOOST_TEST(mrl
.m_info
[1].bytes
== 2*monotonic_buffer_resource::initial_next_buffer_size
+block_slist::header_size
);
323 std::size_t wasted_due_to_alignment
;
324 //For a 2 byte alignment one byte will be wasted from the previous 1 byte allocation
325 std::size_t remaining
= dmbr
.remaining_storage(2u, wasted_due_to_alignment
);
326 BOOST_TEST(wasted_due_to_alignment
== 1u);
327 BOOST_TEST(remaining
== (mrl
.m_info
[1].bytes
- 1u - wasted_due_to_alignment
- block_slist::header_size
));
328 //It should not have allocated
329 remaining_storage
= dmbr
.remaining_storage(1u);
331 //Now try a bigger than next allocation and see if next_buffer_size is doubled.
333 std::size_t next_alloc
= 5*monotonic_buffer_resource::initial_next_buffer_size
;
334 dmbr
.do_allocate(next_alloc
, 1u);
335 BOOST_TEST(mrl
.m_info
.size() == 3u);
336 //The next allocation should be geometrically bigger.
337 BOOST_TEST(mrl
.m_info
[2].bytes
== 8*monotonic_buffer_resource::initial_next_buffer_size
+block_slist::header_size
);
338 remaining_storage
= dmbr
.remaining_storage(1u);
341 //derived_from_monotonic_buffer_resource dmbr(&mrl) is destroyed
342 BOOST_TEST(mrl
.m_mismatches
== 0u);
343 BOOST_TEST(mrl
.m_info
.size() == 0u);
345 //Now use a local buffer
347 boost::move_detail::aligned_storage
348 <monotonic_buffer_resource::initial_next_buffer_size
>::type buf
;
349 //Supply an external buffer
350 derived_from_monotonic_buffer_resource
dmbr(&buf
, sizeof(buf
), &mrl
);
351 BOOST_TEST(dmbr
.remaining_storage(1u) == sizeof(buf
));
352 //Allocate all remaining storage
353 dmbr
.do_allocate(dmbr
.remaining_storage(1u), 1u);
354 //No new allocation should have ocurred
355 BOOST_TEST(mrl
.m_info
.size() == 0u);
356 BOOST_TEST(dmbr
.remaining_storage(1u) == 0u);
358 BOOST_TEST(mrl
.m_mismatches
== 0u);
359 BOOST_TEST(mrl
.m_info
.size() == 0u);
362 void test_do_deallocate()
364 memory_resource_logger mrl
;
365 const std::size_t initial_size
= 1u;
367 derived_from_monotonic_buffer_resource
dmbr(initial_size
, &mrl
);
368 //First test, no buffer
369 const unsigned iterations
= 8;
370 char *bufs
[iterations
];
371 std::size_t sizes
[iterations
];
372 //Test each iteration allocates memory
373 for(unsigned i
= 0; i
!= iterations
; ++i
)
375 sizes
[i
] = dmbr
.remaining_storage()+1;
376 bufs
[i
] = (char*)dmbr
.do_allocate(sizes
[i
], 1);
377 BOOST_TEST(mrl
.m_info
.size() == (i
+1));
379 std::size_t remaining
= dmbr
.remaining_storage();
380 //Test do_deallocate does not release any storage
381 for(unsigned i
= 0; i
!= iterations
; ++i
)
383 dmbr
.do_deallocate(bufs
[i
], sizes
[i
], 1u);
384 BOOST_TEST(mrl
.m_info
.size() == iterations
);
385 BOOST_TEST(remaining
== dmbr
.remaining_storage());
386 BOOST_TEST(mrl
.m_mismatches
== 0u);
391 void test_do_is_equal()
394 //! `this == dynamic_cast<const monotonic_buffer_resource*>(&other)`.
395 memory_resource_logger mrl
;
396 derived_from_monotonic_buffer_resource
dmbr(&mrl
);
397 derived_from_monotonic_buffer_resource
dmbr2(&mrl
);
398 BOOST_TEST(true == dmbr
.do_is_equal(dmbr
));
399 BOOST_TEST(false == dmbr
.do_is_equal(dmbr2
));
400 //A different type should be always different
401 derived_from_memory_resource dmr
;
402 BOOST_TEST(false == dmbr
.do_is_equal(dmr
));
407 memory_resource_logger mrl
;
408 const std::size_t initial_size
= 1u;
409 derived_from_monotonic_buffer_resource
dmbr(initial_size
, &mrl
);
410 //First test, no buffer
411 const unsigned iterations
= 8;
412 //Test each iteration allocates memory
413 for(unsigned i
= 0; i
!= iterations
; ++i
)
415 dmbr
.do_allocate(dmbr
.remaining_storage()+1, 1);
416 BOOST_TEST(mrl
.m_info
.size() == (i
+1));
418 //Release and check memory was released
420 BOOST_TEST(mrl
.m_mismatches
== 0u);
421 BOOST_TEST(mrl
.m_info
.size() == 0u);
424 void test_destructor()
426 memory_resource_logger mrl
;
427 const std::size_t initial_size
= 1u;
429 derived_from_monotonic_buffer_resource
dmbr(initial_size
, &mrl
);
430 //First test, no buffer
431 const unsigned iterations
= 8;
432 //Test each iteration allocates memory
433 for(unsigned i
= 0; i
!= iterations
; ++i
)
435 dmbr
.do_allocate(dmbr
.remaining_storage()+1, 1);
436 BOOST_TEST(mrl
.m_info
.size() == (i
+1));
438 } //dmbr is destroyed, memory should be released
439 BOOST_TEST(mrl
.m_mismatches
== 0u);
440 BOOST_TEST(mrl
.m_info
.size() == 0u);
445 test_block_chain::test_constructor();
446 test_block_chain::test_allocate();
447 test_block_chain::test_release();
448 test_block_chain::test_memory_resource();
449 test_block_chain::test_destructor();
451 test_resource_constructor();
452 test_initial_size_constructor();
453 test_buffer_constructor();
455 test_upstream_resource();
457 test_do_deallocate();
461 return ::boost::report_errors();