/src/mysql-server/include/prealloced_array.h
Line | Count | Source |
1 | | /* Copyright (c) 2013, 2025, Oracle and/or its affiliates. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License, version 2.0, |
5 | | as published by the Free Software Foundation. |
6 | | |
7 | | This program is designed to work with certain software (including |
8 | | but not limited to OpenSSL) that is licensed under separate terms, |
9 | | as designated in a particular file or component or in included license |
10 | | documentation. The authors of MySQL hereby grant you an additional |
11 | | permission to link the program and your derivative works with the |
12 | | separately licensed software that they have either included with |
13 | | the program or referenced in the documentation. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License, version 2.0, for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program; if not, write to the Free Software |
22 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
23 | | |
24 | | #ifndef PREALLOCED_ARRAY_INCLUDED |
25 | | #define PREALLOCED_ARRAY_INCLUDED |
26 | | |
27 | | /** |
28 | | @file include/prealloced_array.h |
29 | | */ |
30 | | |
31 | | #include <assert.h> |
32 | | #include <stddef.h> |
33 | | #include <algorithm> |
34 | | #include <new> |
35 | | #include <type_traits> |
36 | | #include <utility> |
37 | | |
38 | | #include "my_compiler.h" |
39 | | |
40 | | #include "my_inttypes.h" |
41 | | #include "my_sys.h" |
42 | | #include "mysql/psi/psi_memory.h" |
43 | | #include "mysql/service_mysql_alloc.h" |
44 | | |
45 | | /** |
46 | | A typesafe replacement for DYNAMIC_ARRAY. We do our own memory management, |
47 | | and pre-allocate space for a number of elements. The purpose is to |
48 | | pre-allocate enough elements to cover normal use cases, thus saving |
49 | | malloc()/free() overhead. |
50 | | If we run out of space, we use malloc to allocate more space. |
51 | | |
52 | | The interface is chosen to be similar to std::vector. |
53 | | We keep the std::vector property that storage is contiguous. |
54 | | |
55 | | We have fairly low overhead over the inline storage; typically 8 bytes |
56 | | (e.g. Prealloced_array<TABLE *, 4> needs 40 bytes on 64-bit platforms). |
57 | | |
58 | | @remark |
59 | | Unlike DYNAMIC_ARRAY, elements are properly copied |
60 | | (rather than memcpy()d) if the underlying array needs to be expanded. |
61 | | |
62 | | @remark |
63 | | Depending on Has_trivial_destructor, we destroy objects which are |
64 | | removed from the array (including when the array object itself is destroyed). |
65 | | |
66 | | @tparam Element_type The type of the elements of the container. |
67 | | Elements must be copyable or movable. |
68 | | @tparam Prealloc Number of elements to pre-allocate. |
69 | | */ |
70 | | template <typename Element_type, size_t Prealloc> |
71 | | class Prealloced_array { |
72 | | /** |
73 | | Is Element_type trivially destructible? If it is, we don't destroy |
74 | | elements when they are removed from the array or when the array is |
75 | | destroyed. |
76 | | */ |
77 | | static constexpr bool Has_trivial_destructor = |
78 | | std::is_trivially_destructible<Element_type>::value; |
79 | | |
80 | 0 | bool using_inline_buffer() const { return m_inline_size >= 0; }Unexecuted instantiation: Prealloced_array<Mutex_cond_array::Mutex_cond*, 8ul>::using_inline_buffer() const Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::using_inline_buffer() const Unexecuted instantiation: Prealloced_array<malloc_unordered_multimap<long, std::__1::unique_ptr<Owned_gtids::Node, My_free_deleter>, std::__1::hash<long>, std::__1::equal_to<long> >*, 8ul>::using_inline_buffer() const Unexecuted instantiation: Prealloced_array<bool, 8ul>::using_inline_buffer() const Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::using_inline_buffer() const Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::using_inline_buffer() const |
81 | | |
82 | | /** |
83 | | Gets the buffer in use. |
84 | | */ |
85 | 0 | Element_type *buffer() { |
86 | 0 | return using_inline_buffer() ? m_buff : m_ext.m_array_ptr; |
87 | 0 | } Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::buffer() Unexecuted instantiation: Prealloced_array<bool, 8ul>::buffer() Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::buffer() Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::buffer() |
88 | 0 | const Element_type *buffer() const { |
89 | 0 | return using_inline_buffer() ? m_buff : m_ext.m_array_ptr; |
90 | 0 | } Unexecuted instantiation: Prealloced_array<Mutex_cond_array::Mutex_cond*, 8ul>::buffer() const Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::buffer() const Unexecuted instantiation: Prealloced_array<malloc_unordered_multimap<long, std::__1::unique_ptr<Owned_gtids::Node, My_free_deleter>, std::__1::hash<long>, std::__1::equal_to<long> >*, 8ul>::buffer() const |
91 | | |
92 | 0 | void set_size(size_t n) { |
93 | 0 | if (using_inline_buffer()) { |
94 | 0 | m_inline_size = n; |
95 | 0 | } else { |
96 | 0 | m_ext.m_alloced_size = n; |
97 | 0 | } |
98 | 0 | } Unexecuted instantiation: Prealloced_array<bool, 8ul>::set_size(unsigned long) Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::set_size(unsigned long) Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::set_size(unsigned long) |
99 | 0 | void adjust_size(int delta) { |
100 | 0 | if (using_inline_buffer()) { |
101 | 0 | m_inline_size += delta; |
102 | 0 | } else { |
103 | 0 | m_ext.m_alloced_size += delta; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | public: |
108 | | /// Initial capacity of the array. |
109 | | static const size_t initial_capacity = Prealloc; |
110 | | |
111 | | /// Standard typedefs. |
112 | | typedef Element_type value_type; |
113 | | typedef size_t size_type; |
114 | | typedef ptrdiff_t difference_type; |
115 | | |
116 | | typedef Element_type *iterator; |
117 | | typedef const Element_type *const_iterator; |
118 | | |
119 | 0 | explicit Prealloced_array(PSI_memory_key psi_key) : m_psi_key(psi_key) { |
120 | 0 | static_assert(Prealloc != 0, "We do not want a zero-size array."); |
121 | 0 | } |
122 | | |
123 | | /** |
124 | | Initializes (parts of) the array with default values. |
125 | | Using 'Prealloc' for initial_size makes this similar to a raw C array. |
126 | | */ |
127 | | Prealloced_array(PSI_memory_key psi_key, size_t initial_size) |
128 | | : m_psi_key(psi_key) { |
129 | | static_assert(Prealloc != 0, "We do not want a zero-size array."); |
130 | | |
131 | | if (initial_size > Prealloc) { |
132 | | // We avoid using reserve() since it requires Element_type to be copyable. |
133 | | void *mem = |
134 | | my_malloc(m_psi_key, initial_size * element_size(), MYF(MY_WME)); |
135 | | if (!mem) return; |
136 | | m_inline_size = -1; |
137 | | m_ext.m_alloced_size = initial_size; |
138 | | m_ext.m_array_ptr = static_cast<Element_type *>(mem); |
139 | | m_ext.m_alloced_capacity = initial_size; |
140 | | } else { |
141 | | m_inline_size = initial_size; |
142 | | } |
143 | | for (size_t ix = 0; ix < initial_size; ++ix) { |
144 | | Element_type *p = &buffer()[ix]; |
145 | | ::new (p) Element_type(); |
146 | | } |
147 | | } |
148 | | |
149 | | /** |
150 | | An object instance "owns" its array, so we do deep copy here. |
151 | | */ |
152 | | Prealloced_array(const Prealloced_array &that) : m_psi_key(that.m_psi_key) { |
153 | | if (this->reserve(that.capacity())) return; |
154 | | for (const Element_type *p = that.begin(); p != that.end(); ++p) |
155 | | this->push_back(*p); |
156 | | } |
157 | | |
158 | | Prealloced_array(Prealloced_array &&that) : m_psi_key(that.m_psi_key) { |
159 | | *this = std::move(that); |
160 | | } |
161 | | |
162 | | /** |
163 | | Range constructor. |
164 | | |
165 | | Constructs a container with as many elements as the range [first,last), |
166 | | with each element constructed from its corresponding element in that range, |
167 | | in the same order. |
168 | | */ |
169 | | Prealloced_array(PSI_memory_key psi_key, const_iterator first, |
170 | | const_iterator last) |
171 | | : m_psi_key(psi_key) { |
172 | | if (this->reserve(last - first)) return; |
173 | | for (; first != last; ++first) push_back(*first); |
174 | | } |
175 | | |
176 | | Prealloced_array(std::initializer_list<Element_type> elems) |
177 | | : Prealloced_array(PSI_NOT_INSTRUMENTED, elems.begin(), elems.end()) {} |
178 | | |
179 | | /** |
180 | | Copies all the elements from 'that' into this container. |
181 | | Any objects in this container are destroyed first. |
182 | | */ |
183 | | Prealloced_array &operator=(const Prealloced_array &that) { |
184 | | this->clear(); |
185 | | if (this->reserve(that.capacity())) return *this; |
186 | | for (const Element_type *p = that.begin(); p != that.end(); ++p) |
187 | | this->push_back(*p); |
188 | | return *this; |
189 | | } |
190 | | |
191 | | Prealloced_array &operator=(Prealloced_array &&that) { |
192 | | this->clear(); |
193 | | if (!that.using_inline_buffer()) { |
194 | | if (!using_inline_buffer()) my_free(m_ext.m_array_ptr); |
195 | | // The array is on the heap, so we can just grab it. |
196 | | m_ext.m_array_ptr = that.m_ext.m_array_ptr; |
197 | | m_inline_size = -1; |
198 | | m_ext.m_alloced_size = that.m_ext.m_alloced_size; |
199 | | m_ext.m_alloced_capacity = that.m_ext.m_alloced_capacity; |
200 | | that.m_inline_size = 0; |
201 | | } else { |
202 | | // Move over each element. |
203 | | if (this->reserve(that.capacity())) return *this; |
204 | | for (Element_type *p = that.begin(); p != that.end(); ++p) |
205 | | this->push_back(std::move(*p)); |
206 | | that.clear(); |
207 | | } |
208 | | return *this; |
209 | | } |
210 | | |
211 | | /** |
212 | | Runs DTOR on all elements if needed. |
213 | | Deallocates array if we exceeded the Preallocated amount. |
214 | | */ |
215 | 0 | ~Prealloced_array() { |
216 | 0 | if (!Has_trivial_destructor) { |
217 | 0 | clear(); |
218 | 0 | } |
219 | 0 | if (!using_inline_buffer()) my_free(m_ext.m_array_ptr); |
220 | 0 | } |
221 | | |
222 | 0 | size_t capacity() const { |
223 | 0 | return using_inline_buffer() ? Prealloc : m_ext.m_alloced_capacity; |
224 | 0 | } |
225 | 0 | size_t element_size() const { return sizeof(Element_type); } |
226 | | bool empty() const { return size() == 0; } |
227 | 0 | size_t size() const { |
228 | 0 | return using_inline_buffer() ? m_inline_size : m_ext.m_alloced_size; |
229 | 0 | } Unexecuted instantiation: Prealloced_array<Mutex_cond_array::Mutex_cond*, 8ul>::size() const Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::size() const Unexecuted instantiation: Prealloced_array<malloc_unordered_multimap<long, std::__1::unique_ptr<Owned_gtids::Node, My_free_deleter>, std::__1::hash<long>, std::__1::equal_to<long> >*, 8ul>::size() const Unexecuted instantiation: Prealloced_array<bool, 8ul>::size() const Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::size() const Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::size() const |
230 | | |
231 | 0 | Element_type &at(size_t n) { |
232 | 0 | assert(n < size()); |
233 | 0 | return buffer()[n]; |
234 | 0 | } |
235 | | |
236 | 0 | const Element_type &at(size_t n) const { |
237 | 0 | assert(n < size()); |
238 | 0 | return buffer()[n]; |
239 | 0 | } Unexecuted instantiation: Prealloced_array<Mutex_cond_array::Mutex_cond*, 8ul>::at(unsigned long) const Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::at(unsigned long) const Unexecuted instantiation: Prealloced_array<malloc_unordered_multimap<long, std::__1::unique_ptr<Owned_gtids::Node, My_free_deleter>, std::__1::hash<long>, std::__1::equal_to<long> >*, 8ul>::at(unsigned long) const |
240 | | |
241 | 0 | Element_type &operator[](size_t n) { return at(n); } |
242 | 0 | const Element_type &operator[](size_t n) const { return at(n); }Unexecuted instantiation: Prealloced_array<Mutex_cond_array::Mutex_cond*, 8ul>::operator[](unsigned long) const Unexecuted instantiation: Prealloced_array<Gtid_set::Interval*, 8ul>::operator[](unsigned long) const Unexecuted instantiation: Prealloced_array<malloc_unordered_multimap<long, std::__1::unique_ptr<Owned_gtids::Node, My_free_deleter>, std::__1::hash<long>, std::__1::equal_to<long> >*, 8ul>::operator[](unsigned long) const |
243 | | |
244 | | Element_type &back() { return at(size() - 1); } |
245 | | const Element_type &back() const { return at(size() - 1); } |
246 | | |
247 | | Element_type &front() { return at(0); } |
248 | | const Element_type &front() const { return at(0); } |
249 | | |
250 | | /** |
251 | | begin : Returns a pointer to the first element in the array. |
252 | | end : Returns a pointer to the past-the-end element in the array. |
253 | | */ |
254 | 0 | iterator begin() { return buffer(); }Unexecuted instantiation: Prealloced_array<bool, 8ul>::begin() Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::begin() Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::begin() |
255 | 0 | iterator end() { return buffer() + size(); }Unexecuted instantiation: Prealloced_array<bool, 8ul>::end() Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::end() Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::end() |
256 | | const_iterator begin() const { return buffer(); } |
257 | | const_iterator end() const { return buffer() + size(); } |
258 | | /// Returns a constant pointer to the first element in the array. |
259 | | const_iterator cbegin() const { return begin(); } |
260 | | /// Returns a constant pointer to the past-the-end element in the array. |
261 | | const_iterator cend() const { return end(); } |
262 | | |
263 | | /** |
264 | | Assigns a value to an arbitrary element, even where n >= size(). |
265 | | The array is extended with default values if necessary. |
266 | | @retval true if out-of-memory, false otherwise. |
267 | | */ |
268 | | bool assign_at(size_t n, const value_type &val) { |
269 | | if (n < size()) { |
270 | | at(n) = val; |
271 | | return false; |
272 | | } |
273 | | if (reserve(n + 1)) return true; |
274 | | resize(n); |
275 | | return push_back(val); |
276 | | } |
277 | | |
278 | | /** |
279 | | Reserves space for array elements. |
280 | | Copies (or moves, if possible) over existing elements, in case we |
281 | | are re-expanding the array. |
282 | | |
283 | | @param n number of elements. |
284 | | @retval true if out-of-memory, false otherwise. |
285 | | */ |
286 | 0 | bool reserve(size_t n) { |
287 | 0 | if (n <= capacity()) return false; |
288 | | |
289 | 0 | void *mem = my_malloc(m_psi_key, n * element_size(), MYF(MY_WME)); |
290 | 0 | if (!mem) return true; |
291 | 0 | Element_type *new_array = static_cast<Element_type *>(mem); |
292 | | |
293 | | // Move all the existing elements into the new array. |
294 | 0 | size_t old_size = size(); |
295 | 0 | for (size_t ix = 0; ix < old_size; ++ix) { |
296 | 0 | Element_type *new_p = &new_array[ix]; |
297 | 0 | Element_type &old_p = buffer()[ix]; |
298 | 0 | ::new (new_p) Element_type(std::move(old_p)); // Move into new location. |
299 | 0 | if (!Has_trivial_destructor) |
300 | 0 | old_p.~Element_type(); // Destroy the old element. |
301 | 0 | } |
302 | |
|
303 | 0 | if (!using_inline_buffer()) my_free(m_ext.m_array_ptr); |
304 | | |
305 | | // Forget the old array; |
306 | 0 | m_ext.m_alloced_size = old_size; |
307 | 0 | m_inline_size = -1; |
308 | 0 | m_ext.m_array_ptr = new_array; |
309 | 0 | m_ext.m_alloced_capacity = n; |
310 | 0 | return false; |
311 | 0 | } |
312 | | |
313 | | /** |
314 | | Claim memory ownership. |
315 | | |
316 | | @param claim take ownership of memory |
317 | | */ |
318 | | void claim_memory_ownership(bool claim) { |
319 | | if (!using_inline_buffer()) my_claim(m_ext.m_array_ptr, claim); |
320 | | } |
321 | | |
322 | | /** |
323 | | Copies an element into the back of the array. |
324 | | Complexity: Constant (amortized time, reallocation may happen). |
325 | | @return true if out-of-memory, false otherwise |
326 | | */ |
327 | 0 | bool push_back(const Element_type &element) { return emplace_back(element); } |
328 | | |
329 | | /** |
330 | | Copies (or moves, if possible) an element into the back of the array. |
331 | | Complexity: Constant (amortized time, reallocation may happen). |
332 | | @return true if out-of-memory, false otherwise |
333 | | */ |
334 | | bool push_back(Element_type &&element) { |
335 | | return emplace_back(std::move(element)); |
336 | | } |
337 | | |
338 | | /** |
339 | | Constructs an element at the back of the array. |
340 | | Complexity: Constant (amortized time, reallocation may happen). |
341 | | @return true if out-of-memory, false otherwise |
342 | | */ |
343 | | template <typename... Args> |
344 | 0 | bool emplace_back(Args &&...args) { |
345 | 0 | const size_t expansion_factor = 2; |
346 | 0 | if (size() == capacity() && reserve(capacity() * expansion_factor)) |
347 | 0 | return true; |
348 | 0 | Element_type *p = &buffer()[size()]; |
349 | 0 | adjust_size(1); |
350 | 0 | ::new (p) Element_type(std::forward<Args>(args)...); |
351 | 0 | return false; |
352 | 0 | } |
353 | | |
354 | | /** |
355 | | Removes the last element in the array, effectively reducing the |
356 | | container size by one. This destroys the removed element. |
357 | | */ |
358 | | void pop_back() { |
359 | | assert(!empty()); |
360 | | if (!Has_trivial_destructor) back().~Element_type(); |
361 | | adjust_size(-1); |
362 | | } |
363 | | |
364 | | /** |
365 | | The array is extended by inserting a new element before the element at the |
366 | | specified position. |
367 | | |
368 | | This is generally an inefficient operation, since we need to copy |
369 | | elements to make a new "hole" in the array. |
370 | | |
371 | | We use std::rotate to move objects, hence Element_type must be |
372 | | move-assignable and move-constructible. |
373 | | |
374 | | @return an iterator pointing to the inserted value |
375 | | */ |
376 | | iterator insert(const_iterator position, const value_type &val) { |
377 | | return emplace(position, val); |
378 | | } |
379 | | |
380 | | /** |
381 | | The array is extended by inserting a new element before the element at the |
382 | | specified position. The element is moved into the array, if possible. |
383 | | |
384 | | This is generally an inefficient operation, since we need to copy |
385 | | elements to make a new "hole" in the array. |
386 | | |
387 | | We use std::rotate to move objects, hence Element_type must be |
388 | | move-assignable and move-constructible. |
389 | | |
390 | | @return an iterator pointing to the inserted value |
391 | | */ |
392 | | iterator insert(const_iterator position, value_type &&val) { |
393 | | return emplace(position, std::move(val)); |
394 | | } |
395 | | |
396 | | /** |
397 | | The array is extended by inserting a new element before the element at the |
398 | | specified position. The element is constructed in-place. |
399 | | |
400 | | This is generally an inefficient operation, since we need to copy |
401 | | elements to make a new "hole" in the array. |
402 | | |
403 | | We use std::rotate to move objects, hence Element_type must be |
404 | | move-assignable and move-constructible. |
405 | | |
406 | | @return an iterator pointing to the inserted value |
407 | | */ |
408 | | template <typename... Args> |
409 | | iterator emplace(const_iterator position, Args &&...args) { |
410 | | const difference_type n = position - begin(); |
411 | | emplace_back(std::forward<Args>(args)...); |
412 | | std::rotate(begin() + n, end() - 1, end()); |
413 | | return begin() + n; |
414 | | } |
415 | | |
416 | | /** |
417 | | Similar to std::set<>::insert() |
418 | | Extends the array by inserting a new element, but only if it cannot be found |
419 | | in the array already. |
420 | | |
421 | | Assumes that the array is sorted with std::less<Element_type> |
422 | | Insertion using this function will maintain order. |
423 | | |
424 | | @retval A pair, with its member pair::first set an iterator pointing to |
425 | | either the newly inserted element, or to the equivalent element |
426 | | already in the array. The pair::second element is set to true if |
427 | | the new element was inserted, or false if an equivalent element |
428 | | already existed. |
429 | | */ |
430 | | std::pair<iterator, bool> insert_unique(const value_type &val) { |
431 | | std::pair<iterator, iterator> p = std::equal_range(begin(), end(), val); |
432 | | // p.first == p.second means we did not find it. |
433 | | if (p.first == p.second) return std::make_pair(insert(p.first, val), true); |
434 | | return std::make_pair(p.first, false); |
435 | | } |
436 | | |
437 | | /** |
438 | | Similar to std::set<>::erase() |
439 | | Removes a single element from the array by value. |
440 | | The removed element is destroyed. |
441 | | This effectively reduces the container size by one. |
442 | | |
443 | | This is generally an inefficient operation, since we need to copy |
444 | | elements to fill the "hole" in the array. |
445 | | |
446 | | Assumes that the array is sorted with std::less<Element_type>. |
447 | | |
448 | | @retval number of elements removed, 0 or 1. |
449 | | */ |
450 | | size_type erase_unique(const value_type &val) { |
451 | | std::pair<iterator, iterator> p = std::equal_range(begin(), end(), val); |
452 | | if (p.first == p.second) return 0; // Not found |
453 | | erase(p.first); |
454 | | return 1; |
455 | | } |
456 | | |
457 | | /** |
458 | | Similar to std::set<>::count() |
459 | | |
460 | | @note Assumes that array is maintained with insert_unique/erase_unique. |
461 | | |
462 | | @retval 1 if element is found, 0 otherwise. |
463 | | */ |
464 | | size_type count_unique(const value_type &val) const { |
465 | | return std::binary_search(begin(), end(), val); |
466 | | } |
467 | | |
468 | | /** |
469 | | Removes a single element from the array. |
470 | | The removed element is destroyed. |
471 | | This effectively reduces the container size by one. |
472 | | |
473 | | This is generally an inefficient operation, since we need to move |
474 | | or copy elements to fill the "hole" in the array. |
475 | | |
476 | | We use std::move to move objects, hence Element_type must be |
477 | | move-assignable. |
478 | | */ |
479 | | iterator erase(const_iterator position) { |
480 | | assert(position != end()); |
481 | | return erase(position - begin()); |
482 | | } |
483 | | |
484 | | /** |
485 | | Removes a single element from the array. |
486 | | */ |
487 | | iterator erase(size_t ix) { |
488 | | assert(ix < size()); |
489 | | iterator pos = begin() + ix; |
490 | | if (pos + 1 != end()) std::move(pos + 1, end(), pos); |
491 | | pop_back(); |
492 | | return pos; |
493 | | } |
494 | | |
495 | | /** |
496 | | Removes tail elements from the array. |
497 | | The removed elements are destroyed. |
498 | | This effectively reduces the containers size by 'end() - first'. |
499 | | */ |
500 | | void erase_at_end(const_iterator first) { |
501 | | const_iterator last = cend(); |
502 | | const difference_type diff = last - first; |
503 | | if (!Has_trivial_destructor) { |
504 | | for (; first != last; ++first) first->~Element_type(); |
505 | | } |
506 | | adjust_size(-diff); |
507 | | } |
508 | | |
509 | | /** |
510 | | Removes a range of elements from the array. |
511 | | The removed elements are destroyed. |
512 | | This effectively reduces the containers size by 'last - first'. |
513 | | |
514 | | This is generally an inefficient operation, since we need to move |
515 | | or copy elements to fill the "hole" in the array. |
516 | | |
517 | | We use std::move to move objects, hence Element_type must be |
518 | | move-assignable. |
519 | | */ |
520 | | iterator erase(const_iterator first, const_iterator last) { |
521 | | /* |
522 | | std::move() wants non-const input iterators, otherwise it cannot move and |
523 | | must always copy the elements. Convert first and last from const_iterator |
524 | | to iterator. |
525 | | */ |
526 | | iterator start = begin() + (first - cbegin()); |
527 | | iterator stop = begin() + (last - cbegin()); |
528 | | if (first != last) erase_at_end(std::move(stop, end(), start)); |
529 | | return start; |
530 | | } |
531 | | |
532 | | /** |
533 | | Exchanges the content of the container by the content of rhs, which |
534 | | is another vector object of the same type. Sizes may differ. |
535 | | |
536 | | We use std::swap to do the operation. |
537 | | */ |
538 | | void swap(Prealloced_array &rhs) { |
539 | | // Just swap pointers if both arrays have done malloc. |
540 | | if (!using_inline_buffer() && !rhs.using_inline_buffer()) { |
541 | | std::swap(m_ext.m_alloced_size, rhs.m_ext.m_alloced_size); |
542 | | std::swap(m_ext.m_alloced_capacity, rhs.m_ext.m_alloced_capacity); |
543 | | std::swap(m_ext.m_array_ptr, rhs.m_ext.m_array_ptr); |
544 | | std::swap(m_psi_key, rhs.m_psi_key); |
545 | | return; |
546 | | } |
547 | | std::swap(*this, rhs); |
548 | | } |
549 | | |
550 | | /** |
551 | | Requests the container to reduce its capacity to fit its size. |
552 | | */ |
553 | | void shrink_to_fit() { |
554 | | // Cannot shrink the pre-allocated array. |
555 | | if (using_inline_buffer()) return; |
556 | | // No point in swapping. |
557 | | if (size() == capacity()) return; |
558 | | Prealloced_array tmp(m_psi_key, begin(), end()); |
559 | | if (size() <= Prealloc) { |
560 | | /* |
561 | | The elements fit in the pre-allocated array. Destruct the |
562 | | heap-allocated array in this, and copy the elements into the |
563 | | pre-allocated array. |
564 | | */ |
565 | | this->~Prealloced_array(); |
566 | | new (this) Prealloced_array(tmp.m_psi_key, tmp.begin(), tmp.end()); |
567 | | } else { |
568 | | // Both this and tmp have a heap-allocated array. Swap pointers. |
569 | | swap(tmp); |
570 | | } |
571 | | } |
572 | | |
573 | | /** |
574 | | Resizes the container so that it contains n elements. |
575 | | |
576 | | If n is smaller than the current container size, the content is |
577 | | reduced to its first n elements, removing those beyond (and |
578 | | destroying them). |
579 | | |
580 | | If n is greater than the current container size, the content is |
581 | | expanded by inserting at the end as many elements as needed to |
582 | | reach a size of n. If val is specified, the new elements are |
583 | | initialized as copies of val, otherwise, they are |
584 | | value-initialized. |
585 | | |
586 | | If n is also greater than the current container capacity, an automatic |
587 | | reallocation of the allocated storage space takes place. |
588 | | |
589 | | Notice that this function changes the actual content of the |
590 | | container by inserting or erasing elements from it. |
591 | | */ |
592 | | void resize(size_t n, const Element_type &val = Element_type()) { |
593 | | if (n == size()) return; |
594 | | if (n > size()) { |
595 | | if (!reserve(n)) { |
596 | | while (n != size()) push_back(val); |
597 | | } |
598 | | return; |
599 | | } |
600 | | if (!Has_trivial_destructor) { |
601 | | while (n != size()) pop_back(); |
602 | | } |
603 | | set_size(n); |
604 | | } |
605 | | |
606 | | /** |
607 | | Removes (and destroys) all elements. |
608 | | Does not change capacity. |
609 | | */ |
610 | 0 | void clear() { |
611 | 0 | if (!Has_trivial_destructor) { |
612 | 0 | for (Element_type *p = begin(); p != end(); ++p) |
613 | 0 | p->~Element_type(); // Destroy discarded element. |
614 | 0 | } |
615 | 0 | set_size(0); |
616 | 0 | } Unexecuted instantiation: Prealloced_array<bool, 8ul>::clear() Unexecuted instantiation: Prealloced_array<st_plugin_int*, 2ul>::clear() Unexecuted instantiation: Prealloced_array<fileinfo, 100ul>::clear() |
617 | | |
618 | | private: |
619 | | PSI_memory_key m_psi_key; |
620 | | |
621 | | // If >= 0, we're using the inline storage in m_buff, and contains the real |
622 | | // size. If negative, we're using external storage (m_array_ptr), |
623 | | // and m_alloced_size contains the real size. |
624 | | int m_inline_size = 0; |
625 | | |
626 | | // Defined outside the union because we need an initializer to avoid |
627 | | // "may be used uninitialized" for -flto builds, and |
628 | | // MSVC rejects initializers for individual members of an anonymous union. |
629 | | // (otherwise, we'd make it an anonymous struct, to avoid m_ext everywhere). |
630 | | struct External { |
631 | | Element_type *m_array_ptr; |
632 | | size_t m_alloced_size; |
633 | | size_t m_alloced_capacity; |
634 | | }; |
635 | | |
636 | | union { |
637 | | External m_ext{}; |
638 | | Element_type m_buff[Prealloc]; |
639 | | }; |
640 | | }; |
641 | | static_assert(sizeof(Prealloced_array<void *, 4>) <= 40, |
642 | | "Check for no unexpected padding"); |
643 | | |
644 | | #endif // PREALLOCED_ARRAY_INCLUDED |