Coverage Report

Created: 2025-12-31 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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