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 | #define BOOST_CONTAINER_SOURCE |
12 | #include <boost/container/detail/config_begin.hpp> |
13 | #include <boost/container/detail/workaround.hpp> |
14 | |
15 | #include <boost/container/pmr/monotonic_buffer_resource.hpp> |
16 | #include <boost/container/pmr/global_resource.hpp> |
17 | |
18 | #include <boost/container/detail/min_max.hpp> |
19 | #include <boost/intrusive/detail/math.hpp> |
20 | #include <boost/container/throw_exception.hpp> |
21 | #include <new> |
22 | |
23 | |
24 | #include <cstddef> |
25 | |
26 | namespace { |
27 | |
28 | #ifdef BOOST_HAS_INTPTR_T |
29 | typedef boost::uintptr_t uintptr_type; |
30 | #else |
31 | typedef std::size_t uintptr_type; |
32 | #endif |
33 | |
34 | static const std::size_t minimum_buffer_size = 2*sizeof(void*); |
35 | |
36 | } //namespace { |
37 | |
38 | namespace boost { |
39 | namespace container { |
40 | namespace pmr { |
41 | |
42 | void monotonic_buffer_resource::increase_next_buffer() |
43 | { |
44 | m_next_buffer_size = (std::size_t(-1)/2 < m_next_buffer_size) ? std::size_t(-1) : m_next_buffer_size*2; |
45 | } |
46 | |
47 | void monotonic_buffer_resource::increase_next_buffer_at_least_to(std::size_t minimum_size) |
48 | { |
49 | if(m_next_buffer_size < minimum_size){ |
50 | if(bi::detail::is_pow2(x: minimum_size)){ |
51 | m_next_buffer_size = minimum_size; |
52 | } |
53 | else if(std::size_t(-1)/2 < minimum_size){ |
54 | m_next_buffer_size = minimum_size; |
55 | } |
56 | else{ |
57 | m_next_buffer_size = bi::detail::ceil_pow2(x: minimum_size); |
58 | } |
59 | } |
60 | } |
61 | |
62 | monotonic_buffer_resource::monotonic_buffer_resource(memory_resource* upstream) BOOST_NOEXCEPT |
63 | : m_memory_blocks(upstream ? *upstream : *get_default_resource()) |
64 | , m_current_buffer(0) |
65 | , m_current_buffer_size(0u) |
66 | , m_next_buffer_size(initial_next_buffer_size) |
67 | , m_initial_buffer(0) |
68 | , m_initial_buffer_size(0u) |
69 | {} |
70 | |
71 | monotonic_buffer_resource::monotonic_buffer_resource(std::size_t initial_size, memory_resource* upstream) BOOST_NOEXCEPT |
72 | : m_memory_blocks(upstream ? *upstream : *get_default_resource()) |
73 | , m_current_buffer(0) |
74 | , m_current_buffer_size(0u) |
75 | , m_next_buffer_size(minimum_buffer_size) |
76 | , m_initial_buffer(0) |
77 | , m_initial_buffer_size(0u) |
78 | { //In case initial_size is zero |
79 | this->increase_next_buffer_at_least_to(minimum_size: initial_size + !initial_size); |
80 | } |
81 | |
82 | monotonic_buffer_resource::monotonic_buffer_resource(void* buffer, std::size_t buffer_size, memory_resource* upstream) BOOST_NOEXCEPT |
83 | : m_memory_blocks(upstream ? *upstream : *get_default_resource()) |
84 | , m_current_buffer(buffer) |
85 | , m_current_buffer_size(buffer_size) |
86 | , m_next_buffer_size |
87 | (bi::detail::previous_or_equal_pow2 |
88 | (x: boost::container::dtl::max_value(a: buffer_size, b: std::size_t(initial_next_buffer_size)))) |
89 | , m_initial_buffer(buffer) |
90 | , m_initial_buffer_size(buffer_size) |
91 | { this->increase_next_buffer(); } |
92 | |
93 | monotonic_buffer_resource::~monotonic_buffer_resource() |
94 | { this->release(); } |
95 | |
96 | void monotonic_buffer_resource::release() BOOST_NOEXCEPT |
97 | { |
98 | m_memory_blocks.release(); |
99 | m_current_buffer = m_initial_buffer; |
100 | m_current_buffer_size = m_initial_buffer_size; |
101 | m_next_buffer_size = initial_next_buffer_size; |
102 | } |
103 | |
104 | memory_resource* monotonic_buffer_resource::upstream_resource() const BOOST_NOEXCEPT |
105 | { return &m_memory_blocks.upstream_resource(); } |
106 | |
107 | std::size_t monotonic_buffer_resource::remaining_storage(std::size_t alignment, std::size_t &wasted_due_to_alignment) const BOOST_NOEXCEPT |
108 | { |
109 | const uintptr_type up_alignment_minus1 = alignment - 1u; |
110 | const uintptr_type up_alignment_mask = ~up_alignment_minus1; |
111 | const uintptr_type up_addr = uintptr_type(m_current_buffer); |
112 | const uintptr_type up_aligned_addr = (up_addr + up_alignment_minus1) & up_alignment_mask; |
113 | wasted_due_to_alignment = std::size_t(up_aligned_addr - up_addr); |
114 | return m_current_buffer_size <= wasted_due_to_alignment ? 0u : m_current_buffer_size - wasted_due_to_alignment; |
115 | } |
116 | |
117 | std::size_t monotonic_buffer_resource::remaining_storage(std::size_t alignment) const BOOST_NOEXCEPT |
118 | { |
119 | std::size_t ignore_this; |
120 | return this->remaining_storage(alignment, wasted_due_to_alignment&: ignore_this); |
121 | } |
122 | |
123 | const void *monotonic_buffer_resource::current_buffer() const BOOST_NOEXCEPT |
124 | { return m_current_buffer; } |
125 | |
126 | std::size_t monotonic_buffer_resource::next_buffer_size() const BOOST_NOEXCEPT |
127 | { return m_next_buffer_size; } |
128 | |
129 | void *monotonic_buffer_resource::allocate_from_current(std::size_t aligner, std::size_t bytes) |
130 | { |
131 | char * p = (char*)m_current_buffer + aligner; |
132 | m_current_buffer = p + bytes; |
133 | m_current_buffer_size -= aligner + bytes; |
134 | return p; |
135 | } |
136 | |
137 | void* monotonic_buffer_resource::do_allocate(std::size_t bytes, std::size_t alignment) |
138 | { |
139 | if(alignment > memory_resource::max_align){ |
140 | (void)bytes; (void)alignment; |
141 | #if defined(BOOST_CONTAINER_USER_DEFINED_THROW_CALLBACKS) || defined(BOOST_NO_EXCEPTIONS) |
142 | throw_bad_alloc(); |
143 | #else |
144 | throw std::bad_alloc(); |
145 | #endif |
146 | } |
147 | |
148 | //See if there is room in current buffer |
149 | std::size_t aligner = 0u; |
150 | if(this->remaining_storage(alignment, wasted_due_to_alignment&: aligner) < bytes){ |
151 | //The new buffer will be aligned to the strictest alignment so reset |
152 | //the aligner, which was needed for the old buffer. |
153 | aligner = 0u; |
154 | //Update next_buffer_size to at least bytes |
155 | this->increase_next_buffer_at_least_to(minimum_size: bytes); |
156 | //Now allocate and update internal data |
157 | m_current_buffer = (char*)m_memory_blocks.allocate(size: m_next_buffer_size); |
158 | m_current_buffer_size = m_next_buffer_size; |
159 | this->increase_next_buffer(); |
160 | } |
161 | //Enough internal storage, extract from it |
162 | return this->allocate_from_current(aligner, bytes); |
163 | } |
164 | |
165 | void monotonic_buffer_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment) BOOST_NOEXCEPT |
166 | { (void)p; (void)bytes; (void)alignment; } |
167 | |
168 | bool monotonic_buffer_resource::do_is_equal(const memory_resource& other) const BOOST_NOEXCEPT |
169 | { return this == &other; } |
170 | |
171 | } //namespace pmr { |
172 | } //namespace container { |
173 | } //namespace boost { |
174 | |
175 | #include <boost/container/detail/config_end.hpp> |
176 | |