Coverage Report

Created: 2024-09-23 06:29

/src/abseil-cpp/absl/container/inlined_vector.h
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2019 The Abseil Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
// -----------------------------------------------------------------------------
16
// File: inlined_vector.h
17
// -----------------------------------------------------------------------------
18
//
19
// This header file contains the declaration and definition of an "inlined
20
// vector" which behaves in an equivalent fashion to a `std::vector`, except
21
// that storage for small sequences of the vector are provided inline without
22
// requiring any heap allocation.
23
//
24
// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
25
// its template parameters. Instances where `size() <= N` hold contained
26
// elements in inline space. Typically `N` is very small so that sequences that
27
// are expected to be short do not require allocations.
28
//
29
// An `absl::InlinedVector` does not usually require a specific allocator. If
30
// the inlined vector grows beyond its initial constraints, it will need to
31
// allocate (as any normal `std::vector` would). This is usually performed with
32
// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
33
// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
34
35
#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
36
#define ABSL_CONTAINER_INLINED_VECTOR_H_
37
38
#include <algorithm>
39
#include <cstddef>
40
#include <cstdlib>
41
#include <cstring>
42
#include <initializer_list>
43
#include <iterator>
44
#include <memory>
45
#include <type_traits>
46
#include <utility>
47
48
#include "absl/algorithm/algorithm.h"
49
#include "absl/base/internal/throw_delegate.h"
50
#include "absl/base/macros.h"
51
#include "absl/base/optimization.h"
52
#include "absl/base/port.h"
53
#include "absl/container/internal/inlined_vector.h"
54
#include "absl/memory/memory.h"
55
#include "absl/meta/type_traits.h"
56
57
namespace absl {
58
ABSL_NAMESPACE_BEGIN
59
// -----------------------------------------------------------------------------
60
// InlinedVector
61
// -----------------------------------------------------------------------------
62
//
63
// An `absl::InlinedVector` is designed to be a drop-in replacement for
64
// `std::vector` for use cases where the vector's size is sufficiently small
65
// that it can be inlined. If the inlined vector does grow beyond its estimated
66
// capacity, it will trigger an initial allocation on the heap, and will behave
67
// as a `std::vector`. The API of the `absl::InlinedVector` within this file is
68
// designed to cover the same API footprint as covered by `std::vector`.
69
template <typename T, size_t N, typename A = std::allocator<T>>
70
class InlinedVector {
71
  static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity.");
72
73
  using Storage = inlined_vector_internal::Storage<T, N, A>;
74
75
  template <typename TheA>
76
  using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>;
77
  template <typename TheA>
78
  using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
79
  template <typename TheA>
80
  using IsMoveAssignOk = inlined_vector_internal::IsMoveAssignOk<TheA>;
81
82
  template <typename TheA, typename Iterator>
83
  using IteratorValueAdapter =
84
      inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>;
85
  template <typename TheA>
86
  using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>;
87
  template <typename TheA>
88
  using DefaultValueAdapter =
89
      inlined_vector_internal::DefaultValueAdapter<TheA>;
90
91
  template <typename Iterator>
92
  using EnableIfAtLeastForwardIterator = absl::enable_if_t<
93
      inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
94
  template <typename Iterator>
95
  using DisableIfAtLeastForwardIterator = absl::enable_if_t<
96
      !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
97
98
  using MemcpyPolicy = typename Storage::MemcpyPolicy;
99
  using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy;
100
  using ElementwiseConstructPolicy =
101
      typename Storage::ElementwiseConstructPolicy;
102
  using MoveAssignmentPolicy = typename Storage::MoveAssignmentPolicy;
103
104
 public:
105
  using allocator_type = A;
106
  using value_type = inlined_vector_internal::ValueType<A>;
107
  using pointer = inlined_vector_internal::Pointer<A>;
108
  using const_pointer = inlined_vector_internal::ConstPointer<A>;
109
  using size_type = inlined_vector_internal::SizeType<A>;
110
  using difference_type = inlined_vector_internal::DifferenceType<A>;
111
  using reference = inlined_vector_internal::Reference<A>;
112
  using const_reference = inlined_vector_internal::ConstReference<A>;
113
  using iterator = inlined_vector_internal::Iterator<A>;
114
  using const_iterator = inlined_vector_internal::ConstIterator<A>;
115
  using reverse_iterator = inlined_vector_internal::ReverseIterator<A>;
116
  using const_reverse_iterator =
117
      inlined_vector_internal::ConstReverseIterator<A>;
118
119
  // ---------------------------------------------------------------------------
120
  // InlinedVector Constructors and Destructor
121
  // ---------------------------------------------------------------------------
122
123
  // Creates an empty inlined vector with a value-initialized allocator.
124
3.79M
  InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {}
125
126
  // Creates an empty inlined vector with a copy of `allocator`.
127
  explicit InlinedVector(const allocator_type& allocator) noexcept
128
      : storage_(allocator) {}
129
130
  // Creates an inlined vector with `n` copies of `value_type()`.
131
  explicit InlinedVector(size_type n,
132
                         const allocator_type& allocator = allocator_type())
133
      : storage_(allocator) {
134
    storage_.Initialize(DefaultValueAdapter<A>(), n);
135
  }
136
137
  // Creates an inlined vector with `n` copies of `v`.
138
  InlinedVector(size_type n, const_reference v,
139
                const allocator_type& allocator = allocator_type())
140
      : storage_(allocator) {
141
    storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n);
142
  }
143
144
  // Creates an inlined vector with copies of the elements of `list`.
145
  InlinedVector(std::initializer_list<value_type> list,
146
                const allocator_type& allocator = allocator_type())
147
      : InlinedVector(list.begin(), list.end(), allocator) {}
148
149
  // Creates an inlined vector with elements constructed from the provided
150
  // forward iterator range [`first`, `last`).
151
  //
152
  // NOTE: the `enable_if` prevents ambiguous interpretation between a call to
153
  // this constructor with two integral arguments and a call to the above
154
  // `InlinedVector(size_type, const_reference)` constructor.
155
  template <typename ForwardIterator,
156
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
157
  InlinedVector(ForwardIterator first, ForwardIterator last,
158
                const allocator_type& allocator = allocator_type())
159
5.12M
      : storage_(allocator) {
160
5.12M
    storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first),
161
5.12M
                        static_cast<size_t>(std::distance(first, last)));
162
5.12M
  }
163
164
  // Creates an inlined vector with elements constructed from the provided input
165
  // iterator range [`first`, `last`).
166
  template <typename InputIterator,
167
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
168
  InlinedVector(InputIterator first, InputIterator last,
169
                const allocator_type& allocator = allocator_type())
170
      : storage_(allocator) {
171
    std::copy(first, last, std::back_inserter(*this));
172
  }
173
174
  // Creates an inlined vector by copying the contents of `other` using
175
  // `other`'s allocator.
176
  InlinedVector(const InlinedVector& other)
177
      : InlinedVector(other, other.storage_.GetAllocator()) {}
178
179
  // Creates an inlined vector by copying the contents of `other` using the
180
  // provided `allocator`.
181
  InlinedVector(const InlinedVector& other, const allocator_type& allocator)
182
      : storage_(allocator) {
183
    // Fast path: if the other vector is empty, there's nothing for us to do.
184
    if (other.empty()) {
185
      return;
186
    }
187
188
    // Fast path: if the value type is trivially copy constructible, we know the
189
    // allocator doesn't do anything fancy, and there is nothing on the heap
190
    // then we know it is legal for us to simply memcpy the other vector's
191
    // inlined bytes to form our copy of its elements.
192
    if (absl::is_trivially_copy_constructible<value_type>::value &&
193
        std::is_same<A, std::allocator<value_type>>::value &&
194
        !other.storage_.GetIsAllocated()) {
195
      storage_.MemcpyFrom(other.storage_);
196
      return;
197
    }
198
199
    storage_.InitFrom(other.storage_);
200
  }
201
202
  // Creates an inlined vector by moving in the contents of `other` without
203
  // allocating. If `other` contains allocated memory, the newly-created inlined
204
  // vector will take ownership of that memory. However, if `other` does not
205
  // contain allocated memory, the newly-created inlined vector will perform
206
  // element-wise move construction of the contents of `other`.
207
  //
208
  // NOTE: since no allocation is performed for the inlined vector in either
209
  // case, the `noexcept(...)` specification depends on whether moving the
210
  // underlying objects can throw. It is assumed assumed that...
211
  //  a) move constructors should only throw due to allocation failure.
212
  //  b) if `value_type`'s move constructor allocates, it uses the same
213
  //     allocation function as the inlined vector's allocator.
214
  // Thus, the move constructor is non-throwing if the allocator is non-throwing
215
  // or `value_type`'s move constructor is specified as `noexcept`.
216
  InlinedVector(InlinedVector&& other) noexcept(
217
      absl::allocator_is_nothrow<allocator_type>::value ||
218
      std::is_nothrow_move_constructible<value_type>::value)
219
      : storage_(other.storage_.GetAllocator()) {
220
    // Fast path: if the value type can be trivially relocated (i.e. moved from
221
    // and destroyed), and we know the allocator doesn't do anything fancy, then
222
    // it's safe for us to simply adopt the contents of the storage for `other`
223
    // and remove its own reference to them. It's as if we had individually
224
    // move-constructed each value and then destroyed the original.
225
    if (absl::is_trivially_relocatable<value_type>::value &&
226
        std::is_same<A, std::allocator<value_type>>::value) {
227
      storage_.MemcpyFrom(other.storage_);
228
      other.storage_.SetInlinedSize(0);
229
      return;
230
    }
231
232
    // Fast path: if the other vector is on the heap, we can simply take over
233
    // its allocation.
234
    if (other.storage_.GetIsAllocated()) {
235
      storage_.SetAllocation({other.storage_.GetAllocatedData(),
236
                              other.storage_.GetAllocatedCapacity()});
237
      storage_.SetAllocatedSize(other.storage_.GetSize());
238
239
      other.storage_.SetInlinedSize(0);
240
      return;
241
    }
242
243
    // Otherwise we must move each element individually.
244
    IteratorValueAdapter<A, MoveIterator<A>> other_values(
245
        MoveIterator<A>(other.storage_.GetInlinedData()));
246
247
    inlined_vector_internal::ConstructElements<A>(
248
        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
249
        other.storage_.GetSize());
250
251
    storage_.SetInlinedSize(other.storage_.GetSize());
252
  }
253
254
  // Creates an inlined vector by moving in the contents of `other` with a copy
255
  // of `allocator`.
256
  //
257
  // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other`
258
  // contains allocated memory, this move constructor will still allocate. Since
259
  // allocation is performed, this constructor can only be `noexcept` if the
260
  // specified allocator is also `noexcept`.
261
  InlinedVector(
262
      InlinedVector&& other,
263
      const allocator_type&
264
          allocator) noexcept(absl::allocator_is_nothrow<allocator_type>::value)
265
      : storage_(allocator) {
266
    // Fast path: if the value type can be trivially relocated (i.e. moved from
267
    // and destroyed), and we know the allocator doesn't do anything fancy, then
268
    // it's safe for us to simply adopt the contents of the storage for `other`
269
    // and remove its own reference to them. It's as if we had individually
270
    // move-constructed each value and then destroyed the original.
271
    if (absl::is_trivially_relocatable<value_type>::value &&
272
        std::is_same<A, std::allocator<value_type>>::value) {
273
      storage_.MemcpyFrom(other.storage_);
274
      other.storage_.SetInlinedSize(0);
275
      return;
276
    }
277
278
    // Fast path: if the other vector is on the heap and shared the same
279
    // allocator, we can simply take over its allocation.
280
    if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
281
        other.storage_.GetIsAllocated()) {
282
      storage_.SetAllocation({other.storage_.GetAllocatedData(),
283
                              other.storage_.GetAllocatedCapacity()});
284
      storage_.SetAllocatedSize(other.storage_.GetSize());
285
286
      other.storage_.SetInlinedSize(0);
287
      return;
288
    }
289
290
    // Otherwise we must move each element individually.
291
    storage_.Initialize(
292
        IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(other.data())),
293
        other.size());
294
  }
295
296
8.92M
  ~InlinedVector() {}
absl::InlinedVector<absl::LogSink*, 16ul, std::__1::allocator<absl::LogSink*> >::~InlinedVector()
Line
Count
Source
296
3.79M
  ~InlinedVector() {}
absl::InlinedVector<absl::str_format_internal::FormatArgImpl, 4ul, std::__1::allocator<absl::str_format_internal::FormatArgImpl> >::~InlinedVector()
Line
Count
Source
296
5.12M
  ~InlinedVector() {}
297
298
  // ---------------------------------------------------------------------------
299
  // InlinedVector Member Accessors
300
  // ---------------------------------------------------------------------------
301
302
  // `InlinedVector::empty()`
303
  //
304
  // Returns whether the inlined vector contains no elements.
305
  bool empty() const noexcept { return !size(); }
306
307
  // `InlinedVector::size()`
308
  //
309
  // Returns the number of elements in the inlined vector.
310
8.92M
  size_type size() const noexcept { return storage_.GetSize(); }
absl::InlinedVector<absl::LogSink*, 16ul, std::__1::allocator<absl::LogSink*> >::size() const
Line
Count
Source
310
3.79M
  size_type size() const noexcept { return storage_.GetSize(); }
absl::InlinedVector<absl::str_format_internal::FormatArgImpl, 4ul, std::__1::allocator<absl::str_format_internal::FormatArgImpl> >::size() const
Line
Count
Source
310
5.12M
  size_type size() const noexcept { return storage_.GetSize(); }
311
312
  // `InlinedVector::max_size()`
313
  //
314
  // Returns the maximum number of elements the inlined vector can hold.
315
  size_type max_size() const noexcept {
316
    // One bit of the size storage is used to indicate whether the inlined
317
    // vector contains allocated memory. As a result, the maximum size that the
318
    // inlined vector can express is the minimum of the limit of how many
319
    // objects we can allocate and std::numeric_limits<size_type>::max() / 2.
320
    return (std::min)(AllocatorTraits<A>::max_size(storage_.GetAllocator()),
321
                      (std::numeric_limits<size_type>::max)() / 2);
322
  }
323
324
  // `InlinedVector::capacity()`
325
  //
326
  // Returns the number of elements that could be stored in the inlined vector
327
  // without requiring a reallocation.
328
  //
329
  // NOTE: for most inlined vectors, `capacity()` should be equal to the
330
  // template parameter `N`. For inlined vectors which exceed this capacity,
331
  // they will no longer be inlined and `capacity()` will equal the capactity of
332
  // the allocated memory.
333
  size_type capacity() const noexcept {
334
    return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity()
335
                                     : storage_.GetInlinedCapacity();
336
  }
337
338
  // `InlinedVector::data()`
339
  //
340
  // Returns a `pointer` to the elements of the inlined vector. This pointer
341
  // can be used to access and modify the contained elements.
342
  //
343
  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
344
3.79M
  pointer data() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
345
3.79M
    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
346
3.79M
                                     : storage_.GetInlinedData();
347
3.79M
  }
348
349
  // Overload of `InlinedVector::data()` that returns a `const_pointer` to the
350
  // elements of the inlined vector. This pointer can be used to access but not
351
  // modify the contained elements.
352
  //
353
  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
354
5.12M
  const_pointer data() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
355
5.12M
    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
356
5.12M
                                     : storage_.GetInlinedData();
357
5.12M
  }
358
359
  // `InlinedVector::operator[](...)`
360
  //
361
  // Returns a `reference` to the `i`th element of the inlined vector.
362
  reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
363
    ABSL_HARDENING_ASSERT(i < size());
364
    return data()[i];
365
  }
366
367
  // Overload of `InlinedVector::operator[](...)` that returns a
368
  // `const_reference` to the `i`th element of the inlined vector.
369
  const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
370
    ABSL_HARDENING_ASSERT(i < size());
371
    return data()[i];
372
  }
373
374
  // `InlinedVector::at(...)`
375
  //
376
  // Returns a `reference` to the `i`th element of the inlined vector.
377
  //
378
  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
379
  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
380
  reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
381
    if (ABSL_PREDICT_FALSE(i >= size())) {
382
      base_internal::ThrowStdOutOfRange(
383
          "`InlinedVector::at(size_type)` failed bounds check");
384
    }
385
    return data()[i];
386
  }
387
388
  // Overload of `InlinedVector::at(...)` that returns a `const_reference` to
389
  // the `i`th element of the inlined vector.
390
  //
391
  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
392
  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
393
  const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
394
    if (ABSL_PREDICT_FALSE(i >= size())) {
395
      base_internal::ThrowStdOutOfRange(
396
          "`InlinedVector::at(size_type) const` failed bounds check");
397
    }
398
    return data()[i];
399
  }
400
401
  // `InlinedVector::front()`
402
  //
403
  // Returns a `reference` to the first element of the inlined vector.
404
  reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
405
    ABSL_HARDENING_ASSERT(!empty());
406
    return data()[0];
407
  }
408
409
  // Overload of `InlinedVector::front()` that returns a `const_reference` to
410
  // the first element of the inlined vector.
411
  const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
412
    ABSL_HARDENING_ASSERT(!empty());
413
    return data()[0];
414
  }
415
416
  // `InlinedVector::back()`
417
  //
418
  // Returns a `reference` to the last element of the inlined vector.
419
  reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
420
    ABSL_HARDENING_ASSERT(!empty());
421
    return data()[size() - 1];
422
  }
423
424
  // Overload of `InlinedVector::back()` that returns a `const_reference` to the
425
  // last element of the inlined vector.
426
  const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
427
    ABSL_HARDENING_ASSERT(!empty());
428
    return data()[size() - 1];
429
  }
430
431
  // `InlinedVector::begin()`
432
  //
433
  // Returns an `iterator` to the beginning of the inlined vector.
434
  iterator begin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
435
436
  // Overload of `InlinedVector::begin()` that returns a `const_iterator` to
437
  // the beginning of the inlined vector.
438
  const_iterator begin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
439
    return data();
440
  }
441
442
  // `InlinedVector::end()`
443
  //
444
  // Returns an `iterator` to the end of the inlined vector.
445
  iterator end() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
446
    return data() + size();
447
  }
448
449
  // Overload of `InlinedVector::end()` that returns a `const_iterator` to the
450
  // end of the inlined vector.
451
  const_iterator end() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
452
    return data() + size();
453
  }
454
455
  // `InlinedVector::cbegin()`
456
  //
457
  // Returns a `const_iterator` to the beginning of the inlined vector.
458
  const_iterator cbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
459
    return begin();
460
  }
461
462
  // `InlinedVector::cend()`
463
  //
464
  // Returns a `const_iterator` to the end of the inlined vector.
465
  const_iterator cend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
466
    return end();
467
  }
468
469
  // `InlinedVector::rbegin()`
470
  //
471
  // Returns a `reverse_iterator` from the end of the inlined vector.
472
  reverse_iterator rbegin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
473
    return reverse_iterator(end());
474
  }
475
476
  // Overload of `InlinedVector::rbegin()` that returns a
477
  // `const_reverse_iterator` from the end of the inlined vector.
478
  const_reverse_iterator rbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
479
    return const_reverse_iterator(end());
480
  }
481
482
  // `InlinedVector::rend()`
483
  //
484
  // Returns a `reverse_iterator` from the beginning of the inlined vector.
485
  reverse_iterator rend() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
486
    return reverse_iterator(begin());
487
  }
488
489
  // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator`
490
  // from the beginning of the inlined vector.
491
  const_reverse_iterator rend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
492
    return const_reverse_iterator(begin());
493
  }
494
495
  // `InlinedVector::crbegin()`
496
  //
497
  // Returns a `const_reverse_iterator` from the end of the inlined vector.
498
  const_reverse_iterator crbegin() const noexcept
499
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
500
    return rbegin();
501
  }
502
503
  // `InlinedVector::crend()`
504
  //
505
  // Returns a `const_reverse_iterator` from the beginning of the inlined
506
  // vector.
507
  const_reverse_iterator crend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
508
    return rend();
509
  }
510
511
  // `InlinedVector::get_allocator()`
512
  //
513
  // Returns a copy of the inlined vector's allocator.
514
  allocator_type get_allocator() const { return storage_.GetAllocator(); }
515
516
  // ---------------------------------------------------------------------------
517
  // InlinedVector Member Mutators
518
  // ---------------------------------------------------------------------------
519
520
  // `InlinedVector::operator=(...)`
521
  //
522
  // Replaces the elements of the inlined vector with copies of the elements of
523
  // `list`.
524
  InlinedVector& operator=(std::initializer_list<value_type> list) {
525
    assign(list.begin(), list.end());
526
527
    return *this;
528
  }
529
530
  // Overload of `InlinedVector::operator=(...)` that replaces the elements of
531
  // the inlined vector with copies of the elements of `other`.
532
  InlinedVector& operator=(const InlinedVector& other) {
533
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
534
      const_pointer other_data = other.data();
535
      assign(other_data, other_data + other.size());
536
    }
537
538
    return *this;
539
  }
540
541
  // Overload of `InlinedVector::operator=(...)` that moves the elements of
542
  // `other` into the inlined vector.
543
  //
544
  // NOTE: as a result of calling this overload, `other` is left in a valid but
545
  // unspecified state.
546
  InlinedVector& operator=(InlinedVector&& other) {
547
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
548
      MoveAssignment(MoveAssignmentPolicy{}, std::move(other));
549
    }
550
551
    return *this;
552
  }
553
554
  // `InlinedVector::assign(...)`
555
  //
556
  // Replaces the contents of the inlined vector with `n` copies of `v`.
557
  void assign(size_type n, const_reference v) {
558
    storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
559
  }
560
561
  // Overload of `InlinedVector::assign(...)` that replaces the contents of the
562
  // inlined vector with copies of the elements of `list`.
563
  void assign(std::initializer_list<value_type> list) {
564
    assign(list.begin(), list.end());
565
  }
566
567
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
568
  // inlined vector with the range [`first`, `last`).
569
  //
570
  // NOTE: this overload is for iterators that are "forward" category or better.
571
  template <typename ForwardIterator,
572
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
573
  void assign(ForwardIterator first, ForwardIterator last) {
574
    storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
575
                    static_cast<size_t>(std::distance(first, last)));
576
  }
577
578
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
579
  // inlined vector with the range [`first`, `last`).
580
  //
581
  // NOTE: this overload is for iterators that are "input" category.
582
  template <typename InputIterator,
583
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
584
  void assign(InputIterator first, InputIterator last) {
585
    size_type i = 0;
586
    for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
587
      data()[i] = *first;
588
    }
589
590
    erase(data() + i, data() + size());
591
    std::copy(first, last, std::back_inserter(*this));
592
  }
593
594
  // `InlinedVector::resize(...)`
595
  //
596
  // Resizes the inlined vector to contain `n` elements.
597
  //
598
  // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n`
599
  // is larger than `size()`, new elements are value-initialized.
600
  void resize(size_type n) {
601
    ABSL_HARDENING_ASSERT(n <= max_size());
602
    storage_.Resize(DefaultValueAdapter<A>(), n);
603
  }
604
605
  // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
606
  // contain `n` elements.
607
  //
608
  // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n`
609
  // is larger than `size()`, new elements are copied-constructed from `v`.
610
  void resize(size_type n, const_reference v) {
611
    ABSL_HARDENING_ASSERT(n <= max_size());
612
    storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
613
  }
614
615
  // `InlinedVector::insert(...)`
616
  //
617
  // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly
618
  // inserted element.
619
  iterator insert(const_iterator pos,
620
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
621
    return emplace(pos, v);
622
  }
623
624
  // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
625
  // move semantics, returning an `iterator` to the newly inserted element.
626
  iterator insert(const_iterator pos,
627
                  value_type&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
628
    return emplace(pos, std::move(v));
629
  }
630
631
  // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies
632
  // of `v` starting at `pos`, returning an `iterator` pointing to the first of
633
  // the newly inserted elements.
634
  iterator insert(const_iterator pos, size_type n,
635
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
636
    ABSL_HARDENING_ASSERT(pos >= begin());
637
    ABSL_HARDENING_ASSERT(pos <= end());
638
639
    if (ABSL_PREDICT_TRUE(n != 0)) {
640
      value_type dealias = v;
641
      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
642
      // It appears that GCC thinks that since `pos` is a const pointer and may
643
      // point to uninitialized memory at this point, a warning should be
644
      // issued. But `pos` is actually only used to compute an array index to
645
      // write to.
646
#if !defined(__clang__) && defined(__GNUC__)
647
#pragma GCC diagnostic push
648
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
649
#endif
650
      return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
651
                             n);
652
#if !defined(__clang__) && defined(__GNUC__)
653
#pragma GCC diagnostic pop
654
#endif
655
    } else {
656
      return const_cast<iterator>(pos);
657
    }
658
  }
659
660
  // Overload of `InlinedVector::insert(...)` that inserts copies of the
661
  // elements of `list` starting at `pos`, returning an `iterator` pointing to
662
  // the first of the newly inserted elements.
663
  iterator insert(const_iterator pos, std::initializer_list<value_type> list)
664
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
665
    return insert(pos, list.begin(), list.end());
666
  }
667
668
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
669
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
670
  // of the newly inserted elements.
671
  //
672
  // NOTE: this overload is for iterators that are "forward" category or better.
673
  template <typename ForwardIterator,
674
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
675
  iterator insert(const_iterator pos, ForwardIterator first,
676
                  ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
677
    ABSL_HARDENING_ASSERT(pos >= begin());
678
    ABSL_HARDENING_ASSERT(pos <= end());
679
680
    if (ABSL_PREDICT_TRUE(first != last)) {
681
      return storage_.Insert(
682
          pos, IteratorValueAdapter<A, ForwardIterator>(first),
683
          static_cast<size_type>(std::distance(first, last)));
684
    } else {
685
      return const_cast<iterator>(pos);
686
    }
687
  }
688
689
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
690
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
691
  // of the newly inserted elements.
692
  //
693
  // NOTE: this overload is for iterators that are "input" category.
694
  template <typename InputIterator,
695
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
696
  iterator insert(const_iterator pos, InputIterator first,
697
                  InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
698
    ABSL_HARDENING_ASSERT(pos >= begin());
699
    ABSL_HARDENING_ASSERT(pos <= end());
700
701
    size_type index = static_cast<size_type>(std::distance(cbegin(), pos));
702
    for (size_type i = index; first != last; ++i, static_cast<void>(++first)) {
703
      insert(data() + i, *first);
704
    }
705
706
    return iterator(data() + index);
707
  }
708
709
  // `InlinedVector::emplace(...)`
710
  //
711
  // Constructs and inserts an element using `args...` in the inlined vector at
712
  // `pos`, returning an `iterator` pointing to the newly emplaced element.
713
  template <typename... Args>
714
  iterator emplace(const_iterator pos,
715
                   Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
716
    ABSL_HARDENING_ASSERT(pos >= begin());
717
    ABSL_HARDENING_ASSERT(pos <= end());
718
719
    value_type dealias(std::forward<Args>(args)...);
720
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
721
    // It appears that GCC thinks that since `pos` is a const pointer and may
722
    // point to uninitialized memory at this point, a warning should be
723
    // issued. But `pos` is actually only used to compute an array index to
724
    // write to.
725
#if !defined(__clang__) && defined(__GNUC__)
726
#pragma GCC diagnostic push
727
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
728
#endif
729
    return storage_.Insert(pos,
730
                           IteratorValueAdapter<A, MoveIterator<A>>(
731
                               MoveIterator<A>(std::addressof(dealias))),
732
                           1);
733
#if !defined(__clang__) && defined(__GNUC__)
734
#pragma GCC diagnostic pop
735
#endif
736
  }
737
738
  // `InlinedVector::emplace_back(...)`
739
  //
740
  // Constructs and inserts an element using `args...` in the inlined vector at
741
  // `end()`, returning a `reference` to the newly emplaced element.
742
  template <typename... Args>
743
0
  reference emplace_back(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
744
0
    return storage_.EmplaceBack(std::forward<Args>(args)...);
745
0
  }
746
747
  // `InlinedVector::push_back(...)`
748
  //
749
  // Inserts a copy of `v` in the inlined vector at `end()`.
750
0
  void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
751
752
  // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
753
  // using move semantics.
754
  void push_back(value_type&& v) {
755
    static_cast<void>(emplace_back(std::move(v)));
756
  }
757
758
  // `InlinedVector::pop_back()`
759
  //
760
  // Destroys the element at `back()`, reducing the size by `1`.
761
  void pop_back() noexcept {
762
    ABSL_HARDENING_ASSERT(!empty());
763
764
    AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
765
    storage_.SubtractSize(1);
766
  }
767
768
  // `InlinedVector::erase(...)`
769
  //
770
  // Erases the element at `pos`, returning an `iterator` pointing to where the
771
  // erased element was located.
772
  //
773
  // NOTE: may return `end()`, which is not dereferenceable.
774
  iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND {
775
    ABSL_HARDENING_ASSERT(pos >= begin());
776
    ABSL_HARDENING_ASSERT(pos < end());
777
778
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
779
    // It appears that GCC thinks that since `pos` is a const pointer and may
780
    // point to uninitialized memory at this point, a warning should be
781
    // issued. But `pos` is actually only used to compute an array index to
782
    // write to.
783
#if !defined(__clang__) && defined(__GNUC__)
784
#pragma GCC diagnostic push
785
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
786
#pragma GCC diagnostic ignored "-Wuninitialized"
787
#endif
788
    return storage_.Erase(pos, pos + 1);
789
#if !defined(__clang__) && defined(__GNUC__)
790
#pragma GCC diagnostic pop
791
#endif
792
  }
793
794
  // Overload of `InlinedVector::erase(...)` that erases every element in the
795
  // range [`from`, `to`), returning an `iterator` pointing to where the first
796
  // erased element was located.
797
  //
798
  // NOTE: may return `end()`, which is not dereferenceable.
799
  iterator erase(const_iterator from,
800
                 const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND {
801
    ABSL_HARDENING_ASSERT(from >= begin());
802
    ABSL_HARDENING_ASSERT(from <= to);
803
    ABSL_HARDENING_ASSERT(to <= end());
804
805
    if (ABSL_PREDICT_TRUE(from != to)) {
806
      return storage_.Erase(from, to);
807
    } else {
808
      return const_cast<iterator>(from);
809
    }
810
  }
811
812
  // `InlinedVector::clear()`
813
  //
814
  // Destroys all elements in the inlined vector, setting the size to `0` and
815
  // deallocating any held memory.
816
0
  void clear() noexcept {
817
0
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
818
0
        storage_.GetAllocator(), data(), size());
819
0
    storage_.DeallocateIfAllocated();
820
821
0
    storage_.SetInlinedSize(0);
822
0
  }
823
824
  // `InlinedVector::reserve(...)`
825
  //
826
  // Ensures that there is enough room for at least `n` elements.
827
  void reserve(size_type n) { storage_.Reserve(n); }
828
829
  // `InlinedVector::shrink_to_fit()`
830
  //
831
  // Attempts to reduce memory usage by moving elements to (or keeping elements
832
  // in) the smallest available buffer sufficient for containing `size()`
833
  // elements.
834
  //
835
  // If `size()` is sufficiently small, the elements will be moved into (or kept
836
  // in) the inlined space.
837
  void shrink_to_fit() {
838
    if (storage_.GetIsAllocated()) {
839
      storage_.ShrinkToFit();
840
    }
841
  }
842
843
  // `InlinedVector::swap(...)`
844
  //
845
  // Swaps the contents of the inlined vector with `other`.
846
  void swap(InlinedVector& other) {
847
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
848
      storage_.Swap(std::addressof(other.storage_));
849
    }
850
  }
851
852
 private:
853
  template <typename H, typename TheT, size_t TheN, typename TheA>
854
  friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a);
855
856
  void MoveAssignment(MemcpyPolicy, InlinedVector&& other) {
857
    // Assumption check: we shouldn't be told to use memcpy to implement move
858
    // assignment unless we have trivially destructible elements and an
859
    // allocator that does nothing fancy.
860
    static_assert(absl::is_trivially_destructible<value_type>::value, "");
861
    static_assert(std::is_same<A, std::allocator<value_type>>::value, "");
862
863
    // Throw away our existing heap allocation, if any. There is no need to
864
    // destroy the existing elements one by one because we know they are
865
    // trivially destructible.
866
    storage_.DeallocateIfAllocated();
867
868
    // Adopt the other vector's inline elements or heap allocation.
869
    storage_.MemcpyFrom(other.storage_);
870
    other.storage_.SetInlinedSize(0);
871
  }
872
873
  // Destroy our existing elements, if any, and adopt the heap-allocated
874
  // elements of the other vector.
875
  //
876
  // REQUIRES: other.storage_.GetIsAllocated()
877
  void DestroyExistingAndAdopt(InlinedVector&& other) {
878
    ABSL_HARDENING_ASSERT(other.storage_.GetIsAllocated());
879
880
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
881
        storage_.GetAllocator(), data(), size());
882
    storage_.DeallocateIfAllocated();
883
884
    storage_.MemcpyFrom(other.storage_);
885
    other.storage_.SetInlinedSize(0);
886
  }
887
888
  void MoveAssignment(ElementwiseAssignPolicy, InlinedVector&& other) {
889
    // Fast path: if the other vector is on the heap then we don't worry about
890
    // actually move-assigning each element. Instead we only throw away our own
891
    // existing elements and adopt the heap allocation of the other vector.
892
    if (other.storage_.GetIsAllocated()) {
893
      DestroyExistingAndAdopt(std::move(other));
894
      return;
895
    }
896
897
    storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
898
                        MoveIterator<A>(other.storage_.GetInlinedData())),
899
                    other.size());
900
  }
901
902
  void MoveAssignment(ElementwiseConstructPolicy, InlinedVector&& other) {
903
    // Fast path: if the other vector is on the heap then we don't worry about
904
    // actually move-assigning each element. Instead we only throw away our own
905
    // existing elements and adopt the heap allocation of the other vector.
906
    if (other.storage_.GetIsAllocated()) {
907
      DestroyExistingAndAdopt(std::move(other));
908
      return;
909
    }
910
911
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
912
        storage_.GetAllocator(), data(), size());
913
    storage_.DeallocateIfAllocated();
914
915
    IteratorValueAdapter<A, MoveIterator<A>> other_values(
916
        MoveIterator<A>(other.storage_.GetInlinedData()));
917
    inlined_vector_internal::ConstructElements<A>(
918
        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
919
        other.storage_.GetSize());
920
    storage_.SetInlinedSize(other.storage_.GetSize());
921
  }
922
923
  Storage storage_;
924
};
925
926
// -----------------------------------------------------------------------------
927
// InlinedVector Non-Member Functions
928
// -----------------------------------------------------------------------------
929
930
// `swap(...)`
931
//
932
// Swaps the contents of two inlined vectors.
933
template <typename T, size_t N, typename A>
934
void swap(absl::InlinedVector<T, N, A>& a,
935
          absl::InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
936
  a.swap(b);
937
}
938
939
// `operator==(...)`
940
//
941
// Tests for value-equality of two inlined vectors.
942
template <typename T, size_t N, typename A>
943
bool operator==(const absl::InlinedVector<T, N, A>& a,
944
                const absl::InlinedVector<T, N, A>& b) {
945
  auto a_data = a.data();
946
  auto b_data = b.data();
947
  return std::equal(a_data, a_data + a.size(), b_data, b_data + b.size());
948
}
949
950
// `operator!=(...)`
951
//
952
// Tests for value-inequality of two inlined vectors.
953
template <typename T, size_t N, typename A>
954
bool operator!=(const absl::InlinedVector<T, N, A>& a,
955
                const absl::InlinedVector<T, N, A>& b) {
956
  return !(a == b);
957
}
958
959
// `operator<(...)`
960
//
961
// Tests whether the value of an inlined vector is less than the value of
962
// another inlined vector using a lexicographical comparison algorithm.
963
template <typename T, size_t N, typename A>
964
bool operator<(const absl::InlinedVector<T, N, A>& a,
965
               const absl::InlinedVector<T, N, A>& b) {
966
  auto a_data = a.data();
967
  auto b_data = b.data();
968
  return std::lexicographical_compare(a_data, a_data + a.size(), b_data,
969
                                      b_data + b.size());
970
}
971
972
// `operator>(...)`
973
//
974
// Tests whether the value of an inlined vector is greater than the value of
975
// another inlined vector using a lexicographical comparison algorithm.
976
template <typename T, size_t N, typename A>
977
bool operator>(const absl::InlinedVector<T, N, A>& a,
978
               const absl::InlinedVector<T, N, A>& b) {
979
  return b < a;
980
}
981
982
// `operator<=(...)`
983
//
984
// Tests whether the value of an inlined vector is less than or equal to the
985
// value of another inlined vector using a lexicographical comparison algorithm.
986
template <typename T, size_t N, typename A>
987
bool operator<=(const absl::InlinedVector<T, N, A>& a,
988
                const absl::InlinedVector<T, N, A>& b) {
989
  return !(b < a);
990
}
991
992
// `operator>=(...)`
993
//
994
// Tests whether the value of an inlined vector is greater than or equal to the
995
// value of another inlined vector using a lexicographical comparison algorithm.
996
template <typename T, size_t N, typename A>
997
bool operator>=(const absl::InlinedVector<T, N, A>& a,
998
                const absl::InlinedVector<T, N, A>& b) {
999
  return !(a < b);
1000
}
1001
1002
// `AbslHashValue(...)`
1003
//
1004
// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to
1005
// call this directly.
1006
template <typename H, typename T, size_t N, typename A>
1007
H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) {
1008
  auto size = a.size();
1009
  return H::combine(H::combine_contiguous(std::move(h), a.data(), size), size);
1010
}
1011
1012
ABSL_NAMESPACE_END
1013
}  // namespace absl
1014
1015
#endif  // ABSL_CONTAINER_INLINED_VECTOR_H_