Coverage Report

Created: 2026-02-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/container/inlined_vector.h
Line
Count
Source
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/attributes.h"
50
#include "absl/base/internal/iterator_traits.h"
51
#include "absl/base/macros.h"
52
#include "absl/base/optimization.h"
53
#include "absl/base/port.h"
54
#include "absl/base/throw_delegate.h"
55
#include "absl/container/internal/inlined_vector.h"
56
#include "absl/hash/internal/weakly_mixed_integer.h"
57
#include "absl/memory/memory.h"
58
#include "absl/meta/type_traits.h"
59
60
namespace absl {
61
ABSL_NAMESPACE_BEGIN
62
// -----------------------------------------------------------------------------
63
// InlinedVector
64
// -----------------------------------------------------------------------------
65
//
66
// An `absl::InlinedVector` is designed to be a drop-in replacement for
67
// `std::vector` for use cases where the vector's size is sufficiently small
68
// that it can be inlined. If the inlined vector does grow beyond its estimated
69
// capacity, it will trigger an initial allocation on the heap, and will behave
70
// as a `std::vector`. The API of the `absl::InlinedVector` within this file is
71
// designed to cover the same API footprint as covered by `std::vector`.
72
template <typename T, size_t N, typename A = std::allocator<T>>
73
class ABSL_ATTRIBUTE_WARN_UNUSED InlinedVector {
74
  static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity.");
75
76
  using Storage = inlined_vector_internal::Storage<T, N, A>;
77
78
  template <typename TheA>
79
  using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>;
80
  template <typename TheA>
81
  using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
82
  template <typename TheA>
83
  using IsMoveAssignOk = inlined_vector_internal::IsMoveAssignOk<TheA>;
84
85
  template <typename TheA, typename Iterator>
86
  using IteratorValueAdapter =
87
      inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>;
88
  template <typename TheA>
89
  using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>;
90
  template <typename TheA>
91
  using DefaultValueAdapter =
92
      inlined_vector_internal::DefaultValueAdapter<TheA>;
93
94
  template <typename Iterator>
95
  using EnableIfAtLeastForwardIterator = std::enable_if_t<
96
      base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
97
  template <typename Iterator>
98
  using DisableIfAtLeastForwardIterator = std::enable_if_t<
99
      !base_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
100
101
  using MemcpyPolicy = typename Storage::MemcpyPolicy;
102
  using ElementwiseAssignPolicy = typename Storage::ElementwiseAssignPolicy;
103
  using ElementwiseConstructPolicy =
104
      typename Storage::ElementwiseConstructPolicy;
105
  using MoveAssignmentPolicy = typename Storage::MoveAssignmentPolicy;
106
107
 public:
108
  using allocator_type = A;
109
  using value_type = inlined_vector_internal::ValueType<A>;
110
  using pointer = inlined_vector_internal::Pointer<A>;
111
  using const_pointer = inlined_vector_internal::ConstPointer<A>;
112
  using size_type = inlined_vector_internal::SizeType<A>;
113
  using difference_type = inlined_vector_internal::DifferenceType<A>;
114
  using reference = inlined_vector_internal::Reference<A>;
115
  using const_reference = inlined_vector_internal::ConstReference<A>;
116
  using iterator = inlined_vector_internal::Iterator<A>;
117
  using const_iterator = inlined_vector_internal::ConstIterator<A>;
118
  using reverse_iterator = inlined_vector_internal::ReverseIterator<A>;
119
  using const_reverse_iterator =
120
      inlined_vector_internal::ConstReverseIterator<A>;
121
122
  // ---------------------------------------------------------------------------
123
  // InlinedVector Constructors and Destructor
124
  // ---------------------------------------------------------------------------
125
126
  // Creates an empty inlined vector with a value-initialized allocator.
127
0
  InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {}
128
129
  // Creates an empty inlined vector with a copy of `allocator`.
130
  explicit InlinedVector(const allocator_type& allocator) noexcept
131
      : storage_(allocator) {}
132
133
  // Creates an inlined vector with `n` copies of `value_type()`.
134
  explicit InlinedVector(size_type n,
135
                         const allocator_type& allocator = allocator_type())
136
      : storage_(allocator) {
137
    storage_.Initialize(DefaultValueAdapter<A>(), n);
138
  }
139
140
  // Creates an inlined vector with `n` copies of `v`.
141
  InlinedVector(size_type n, const_reference v,
142
                const allocator_type& allocator = allocator_type())
143
      : storage_(allocator) {
144
    storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n);
145
  }
146
147
  // Creates an inlined vector with copies of the elements of `list`.
148
  InlinedVector(std::initializer_list<value_type> list,
149
                const allocator_type& allocator = allocator_type())
150
      : InlinedVector(list.begin(), list.end(), allocator) {}
151
152
  // Creates an inlined vector with elements constructed from the provided
153
  // forward iterator range [`first`, `last`).
154
  //
155
  // NOTE: the `enable_if` prevents ambiguous interpretation between a call to
156
  // this constructor with two integral arguments and a call to the above
157
  // `InlinedVector(size_type, const_reference)` constructor.
158
  template <typename ForwardIterator,
159
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
160
  InlinedVector(ForwardIterator first, ForwardIterator last,
161
                const allocator_type& allocator = allocator_type())
162
0
      : storage_(allocator) {
163
0
    storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first),
164
0
                        static_cast<size_t>(std::distance(first, last)));
165
0
  }
166
167
  // Creates an inlined vector with elements constructed from the provided input
168
  // iterator range [`first`, `last`).
169
  template <typename InputIterator,
170
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
171
  InlinedVector(InputIterator first, InputIterator last,
172
                const allocator_type& allocator = allocator_type())
173
      : storage_(allocator) {
174
    std::copy(first, last, std::back_inserter(*this));
175
  }
176
177
  // Creates an inlined vector by copying the contents of `other` using
178
  // `other`'s allocator.
179
  InlinedVector(const InlinedVector& other)
180
      : InlinedVector(other, other.storage_.GetAllocator()) {}
181
182
  // Creates an inlined vector by copying the contents of `other` using the
183
  // provided `allocator`.
184
  InlinedVector(const InlinedVector& other, const allocator_type& allocator)
185
      : storage_(allocator) {
186
    // Fast path: if the other vector is empty, there's nothing for us to do.
187
    if (other.empty()) {
188
      return;
189
    }
190
191
    // Fast path: if the value type is trivially copy constructible, we know the
192
    // allocator doesn't do anything fancy, and there is nothing on the heap
193
    // then we know it is legal for us to simply memcpy the other vector's
194
    // inlined bytes to form our copy of its elements.
195
    if (absl::is_trivially_copy_constructible<value_type>::value &&
196
        std::is_same<A, std::allocator<value_type>>::value &&
197
        !other.storage_.GetIsAllocated()) {
198
      storage_.MemcpyFrom(other.storage_);
199
      return;
200
    }
201
202
    storage_.InitFrom(other.storage_);
203
  }
204
205
  // Creates an inlined vector by moving in the contents of `other` without
206
  // allocating. If `other` contains allocated memory, the newly-created inlined
207
  // vector will take ownership of that memory. However, if `other` does not
208
  // contain allocated memory, the newly-created inlined vector will perform
209
  // element-wise move construction of the contents of `other`.
210
  //
211
  // NOTE: since no allocation is performed for the inlined vector in either
212
  // case, the `noexcept(...)` specification depends on whether moving the
213
  // underlying objects can throw. It is assumed assumed that...
214
  //  a) move constructors should only throw due to allocation failure.
215
  //  b) if `value_type`'s move constructor allocates, it uses the same
216
  //     allocation function as the inlined vector's allocator.
217
  // Thus, the move constructor is non-throwing if the allocator is non-throwing
218
  // or `value_type`'s move constructor is specified as `noexcept`.
219
  InlinedVector(InlinedVector&& other) noexcept(
220
      absl::allocator_is_nothrow<allocator_type>::value ||
221
      std::is_nothrow_move_constructible<value_type>::value)
222
      : storage_(other.storage_.GetAllocator()) {
223
    // Fast path: if the value type can be trivially relocated (i.e. moved from
224
    // and destroyed), and we know the allocator doesn't do anything fancy, then
225
    // it's safe for us to simply adopt the contents of the storage for `other`
226
    // and remove its own reference to them. It's as if we had individually
227
    // move-constructed each value and then destroyed the original.
228
    if (absl::is_trivially_relocatable<value_type>::value &&
229
        std::is_same<A, std::allocator<value_type>>::value) {
230
      storage_.MemcpyFrom(other.storage_);
231
      other.storage_.SetInlinedSize(0);
232
      return;
233
    }
234
235
    // Fast path: if the other vector is on the heap, we can simply take over
236
    // its allocation.
237
    if (other.storage_.GetIsAllocated()) {
238
      storage_.SetAllocation({other.storage_.GetAllocatedData(),
239
                              other.storage_.GetAllocatedCapacity()});
240
      storage_.SetAllocatedSize(other.storage_.GetSize());
241
242
      other.storage_.SetInlinedSize(0);
243
      return;
244
    }
245
246
    // Otherwise we must move each element individually.
247
    IteratorValueAdapter<A, MoveIterator<A>> other_values(
248
        MoveIterator<A>(other.storage_.GetInlinedData()));
249
250
    inlined_vector_internal::ConstructElements<A>(
251
        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
252
        other.storage_.GetSize());
253
254
    storage_.SetInlinedSize(other.storage_.GetSize());
255
  }
256
257
  // Creates an inlined vector by moving in the contents of `other` with a copy
258
  // of `allocator`.
259
  //
260
  // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other`
261
  // contains allocated memory, this move constructor will still allocate. Since
262
  // allocation is performed, this constructor can only be `noexcept` if the
263
  // specified allocator is also `noexcept`.
264
  InlinedVector(
265
      InlinedVector&& other,
266
      const allocator_type&
267
          allocator) noexcept(absl::allocator_is_nothrow<allocator_type>::value)
268
      : storage_(allocator) {
269
    // Fast path: if the value type can be trivially relocated (i.e. moved from
270
    // and destroyed), and we know the allocator doesn't do anything fancy, then
271
    // it's safe for us to simply adopt the contents of the storage for `other`
272
    // and remove its own reference to them. It's as if we had individually
273
    // move-constructed each value and then destroyed the original.
274
    if (absl::is_trivially_relocatable<value_type>::value &&
275
        std::is_same<A, std::allocator<value_type>>::value) {
276
      storage_.MemcpyFrom(other.storage_);
277
      other.storage_.SetInlinedSize(0);
278
      return;
279
    }
280
281
    // Fast path: if the other vector is on the heap and shared the same
282
    // allocator, we can simply take over its allocation.
283
    if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
284
        other.storage_.GetIsAllocated()) {
285
      storage_.SetAllocation({other.storage_.GetAllocatedData(),
286
                              other.storage_.GetAllocatedCapacity()});
287
      storage_.SetAllocatedSize(other.storage_.GetSize());
288
289
      other.storage_.SetInlinedSize(0);
290
      return;
291
    }
292
293
    // Otherwise we must move each element individually.
294
    storage_.Initialize(
295
        IteratorValueAdapter<A, MoveIterator<A>>(MoveIterator<A>(other.data())),
296
        other.size());
297
  }
298
299
0
  ~InlinedVector() {}
Unexecuted instantiation: absl::InlinedVector<absl::LogSink*, 16ul, std::__1::allocator<absl::LogSink*> >::~InlinedVector()
Unexecuted instantiation: absl::InlinedVector<absl::str_format_internal::FormatArgImpl, 4ul, std::__1::allocator<absl::str_format_internal::FormatArgImpl> >::~InlinedVector()
300
301
  // ---------------------------------------------------------------------------
302
  // InlinedVector Member Accessors
303
  // ---------------------------------------------------------------------------
304
305
  // `InlinedVector::empty()`
306
  //
307
  // Returns whether the inlined vector contains no elements.
308
  bool empty() const noexcept { return !size(); }
309
310
  // `InlinedVector::size()`
311
  //
312
  // Returns the number of elements in the inlined vector.
313
0
  size_type size() const noexcept { return storage_.GetSize(); }
Unexecuted instantiation: absl::InlinedVector<absl::LogSink*, 16ul, std::__1::allocator<absl::LogSink*> >::size() const
Unexecuted instantiation: absl::InlinedVector<absl::str_format_internal::FormatArgImpl, 4ul, std::__1::allocator<absl::str_format_internal::FormatArgImpl> >::size() const
314
315
  // `InlinedVector::max_size()`
316
  //
317
  // Returns the maximum number of elements the inlined vector can hold.
318
  size_type max_size() const noexcept {
319
    // One bit of the size storage is used to indicate whether the inlined
320
    // vector contains allocated memory. As a result, the maximum size that the
321
    // inlined vector can express is the minimum of the limit of how many
322
    // objects we can allocate and std::numeric_limits<size_type>::max() / 2.
323
    return (std::min)(AllocatorTraits<A>::max_size(storage_.GetAllocator()),
324
                      (std::numeric_limits<size_type>::max)() / 2);
325
  }
326
327
  // `InlinedVector::capacity()`
328
  //
329
  // Returns the number of elements that could be stored in the inlined vector
330
  // without requiring a reallocation.
331
  //
332
  // NOTE: for most inlined vectors, `capacity()` should be equal to the
333
  // template parameter `N`. For inlined vectors which exceed this capacity,
334
  // they will no longer be inlined and `capacity()` will equal the capactity of
335
  // the allocated memory.
336
  size_type capacity() const noexcept {
337
    return storage_.GetIsAllocated() ? storage_.GetAllocatedCapacity()
338
                                     : storage_.GetInlinedCapacity();
339
  }
340
341
  // `InlinedVector::data()`
342
  //
343
  // Returns a `pointer` to the elements of the inlined vector. This pointer
344
  // can be used to access and modify the contained elements.
345
  //
346
  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
347
0
  pointer data() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
348
0
    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
349
0
                                     : storage_.GetInlinedData();
350
0
  }
351
352
  // Overload of `InlinedVector::data()` that returns a `const_pointer` to the
353
  // elements of the inlined vector. This pointer can be used to access but not
354
  // modify the contained elements.
355
  //
356
  // NOTE: only elements within [`data()`, `data() + size()`) are valid.
357
0
  const_pointer data() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
358
0
    return storage_.GetIsAllocated() ? storage_.GetAllocatedData()
359
0
                                     : storage_.GetInlinedData();
360
0
  }
361
362
  // `InlinedVector::operator[](...)`
363
  //
364
  // Returns a `reference` to the `i`th element of the inlined vector.
365
  reference operator[](size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
366
    ABSL_HARDENING_ASSERT(i < size());
367
    return data()[i];
368
  }
369
370
  // Overload of `InlinedVector::operator[](...)` that returns a
371
  // `const_reference` to the `i`th element of the inlined vector.
372
  const_reference operator[](size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
373
    ABSL_HARDENING_ASSERT(i < size());
374
    return data()[i];
375
  }
376
377
  // `InlinedVector::at(...)`
378
  //
379
  // Returns a `reference` to the `i`th element of the inlined vector.
380
  //
381
  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
382
  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
383
  reference at(size_type i) ABSL_ATTRIBUTE_LIFETIME_BOUND {
384
    if (ABSL_PREDICT_FALSE(i >= size())) {
385
      ThrowStdOutOfRange("`InlinedVector::at(size_type)` failed bounds check");
386
    }
387
    return data()[i];
388
  }
389
390
  // Overload of `InlinedVector::at(...)` that returns a `const_reference` to
391
  // the `i`th element of the inlined vector.
392
  //
393
  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
394
  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
395
  const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
396
    if (ABSL_PREDICT_FALSE(i >= size())) {
397
      ThrowStdOutOfRange(
398
          "`InlinedVector::at(size_type) const` failed bounds check");
399
    }
400
    return data()[i];
401
  }
402
403
  // `InlinedVector::front()`
404
  //
405
  // Returns a `reference` to the first element of the inlined vector.
406
  reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
407
    ABSL_HARDENING_ASSERT(!empty());
408
    return data()[0];
409
  }
410
411
  // Overload of `InlinedVector::front()` that returns a `const_reference` to
412
  // the first element of the inlined vector.
413
  const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
414
    ABSL_HARDENING_ASSERT(!empty());
415
    return data()[0];
416
  }
417
418
  // `InlinedVector::back()`
419
  //
420
  // Returns a `reference` to the last element of the inlined vector.
421
  reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
422
    ABSL_HARDENING_ASSERT(!empty());
423
    return data()[size() - 1];
424
  }
425
426
  // Overload of `InlinedVector::back()` that returns a `const_reference` to the
427
  // last element of the inlined vector.
428
  const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
429
    ABSL_HARDENING_ASSERT(!empty());
430
    return data()[size() - 1];
431
  }
432
433
  // `InlinedVector::begin()`
434
  //
435
  // Returns an `iterator` to the beginning of the inlined vector.
436
  iterator begin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
437
438
  // Overload of `InlinedVector::begin()` that returns a `const_iterator` to
439
  // the beginning of the inlined vector.
440
  const_iterator begin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
441
    return data();
442
  }
443
444
  // `InlinedVector::end()`
445
  //
446
  // Returns an `iterator` to the end of the inlined vector.
447
  iterator end() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
448
    return data() + size();
449
  }
450
451
  // Overload of `InlinedVector::end()` that returns a `const_iterator` to the
452
  // end of the inlined vector.
453
  const_iterator end() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
454
    return data() + size();
455
  }
456
457
  // `InlinedVector::cbegin()`
458
  //
459
  // Returns a `const_iterator` to the beginning of the inlined vector.
460
  const_iterator cbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
461
    return begin();
462
  }
463
464
  // `InlinedVector::cend()`
465
  //
466
  // Returns a `const_iterator` to the end of the inlined vector.
467
  const_iterator cend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
468
    return end();
469
  }
470
471
  // `InlinedVector::rbegin()`
472
  //
473
  // Returns a `reverse_iterator` from the end of the inlined vector.
474
  reverse_iterator rbegin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
475
    return reverse_iterator(end());
476
  }
477
478
  // Overload of `InlinedVector::rbegin()` that returns a
479
  // `const_reverse_iterator` from the end of the inlined vector.
480
  const_reverse_iterator rbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
481
    return const_reverse_iterator(end());
482
  }
483
484
  // `InlinedVector::rend()`
485
  //
486
  // Returns a `reverse_iterator` from the beginning of the inlined vector.
487
  reverse_iterator rend() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
488
    return reverse_iterator(begin());
489
  }
490
491
  // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator`
492
  // from the beginning of the inlined vector.
493
  const_reverse_iterator rend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
494
    return const_reverse_iterator(begin());
495
  }
496
497
  // `InlinedVector::crbegin()`
498
  //
499
  // Returns a `const_reverse_iterator` from the end of the inlined vector.
500
  const_reverse_iterator crbegin() const noexcept
501
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
502
    return rbegin();
503
  }
504
505
  // `InlinedVector::crend()`
506
  //
507
  // Returns a `const_reverse_iterator` from the beginning of the inlined
508
  // vector.
509
  const_reverse_iterator crend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
510
    return rend();
511
  }
512
513
  // `InlinedVector::get_allocator()`
514
  //
515
  // Returns a copy of the inlined vector's allocator.
516
  allocator_type get_allocator() const { return storage_.GetAllocator(); }
517
518
  // ---------------------------------------------------------------------------
519
  // InlinedVector Member Mutators
520
  // ---------------------------------------------------------------------------
521
522
  // `InlinedVector::operator=(...)`
523
  //
524
  // Replaces the elements of the inlined vector with copies of the elements of
525
  // `list`.
526
  InlinedVector& operator=(std::initializer_list<value_type> list) {
527
    assign(list.begin(), list.end());
528
529
    return *this;
530
  }
531
532
  // Overload of `InlinedVector::operator=(...)` that replaces the elements of
533
  // the inlined vector with copies of the elements of `other`.
534
  InlinedVector& operator=(const InlinedVector& other) {
535
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
536
      const_pointer other_data = other.data();
537
      assign(other_data, other_data + other.size());
538
    }
539
540
    return *this;
541
  }
542
543
  // Overload of `InlinedVector::operator=(...)` that moves the elements of
544
  // `other` into the inlined vector.
545
  //
546
  // NOTE: as a result of calling this overload, `other` is left in a valid but
547
  // unspecified state.
548
  InlinedVector& operator=(InlinedVector&& other) {
549
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
550
      MoveAssignment(MoveAssignmentPolicy{}, std::move(other));
551
    }
552
553
    return *this;
554
  }
555
556
  // `InlinedVector::assign(...)`
557
  //
558
  // Replaces the contents of the inlined vector with `n` copies of `v`.
559
  void assign(size_type n, const_reference v) {
560
    storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
561
  }
562
563
  // Overload of `InlinedVector::assign(...)` that replaces the contents of the
564
  // inlined vector with copies of the elements of `list`.
565
  void assign(std::initializer_list<value_type> list) {
566
    assign(list.begin(), list.end());
567
  }
568
569
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
570
  // inlined vector with the range [`first`, `last`).
571
  //
572
  // NOTE: this overload is for iterators that are "forward" category or better.
573
  template <typename ForwardIterator,
574
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
575
  void assign(ForwardIterator first, ForwardIterator last) {
576
    storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
577
                    static_cast<size_t>(std::distance(first, last)));
578
  }
579
580
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
581
  // inlined vector with the range [`first`, `last`).
582
  //
583
  // NOTE: this overload is for iterators that are "input" category.
584
  template <typename InputIterator,
585
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
586
  void assign(InputIterator first, InputIterator last) {
587
    size_type i = 0;
588
    for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
589
      data()[i] = *first;
590
    }
591
592
    erase(data() + i, data() + size());
593
    std::copy(first, last, std::back_inserter(*this));
594
  }
595
596
  // `InlinedVector::resize(...)`
597
  //
598
  // Resizes the inlined vector to contain `n` elements.
599
  //
600
  // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n`
601
  // is larger than `size()`, new elements are value-initialized.
602
  void resize(size_type n) {
603
    ABSL_HARDENING_ASSERT(n <= max_size());
604
    storage_.Resize(DefaultValueAdapter<A>(), n);
605
  }
606
607
  // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
608
  // contain `n` elements.
609
  //
610
  // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n`
611
  // is larger than `size()`, new elements are copied-constructed from `v`.
612
  void resize(size_type n, const_reference v) {
613
    ABSL_HARDENING_ASSERT(n <= max_size());
614
    storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
615
  }
616
617
  // `InlinedVector::insert(...)`
618
  //
619
  // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly
620
  // inserted element.
621
  iterator insert(const_iterator pos,
622
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
623
    return emplace(pos, v);
624
  }
625
626
  // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
627
  // move semantics, returning an `iterator` to the newly inserted element.
628
  iterator insert(const_iterator pos,
629
                  value_type&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
630
    return emplace(pos, std::move(v));
631
  }
632
633
  // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies
634
  // of `v` starting at `pos`, returning an `iterator` pointing to the first of
635
  // the newly inserted elements.
636
  iterator insert(const_iterator pos, size_type n,
637
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
638
    ABSL_HARDENING_ASSERT(pos >= begin());
639
    ABSL_HARDENING_ASSERT(pos <= end());
640
641
    if (ABSL_PREDICT_TRUE(n != 0)) {
642
      value_type dealias = v;
643
      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
644
      // It appears that GCC thinks that since `pos` is a const pointer and may
645
      // point to uninitialized memory at this point, a warning should be
646
      // issued. But `pos` is actually only used to compute an array index to
647
      // write to.
648
#if !defined(__clang__) && defined(__GNUC__)
649
#pragma GCC diagnostic push
650
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
651
#endif
652
      return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
653
                             n);
654
#if !defined(__clang__) && defined(__GNUC__)
655
#pragma GCC diagnostic pop
656
#endif
657
    } else {
658
      return const_cast<iterator>(pos);
659
    }
660
  }
661
662
  // Overload of `InlinedVector::insert(...)` that inserts copies of the
663
  // elements of `list` starting at `pos`, returning an `iterator` pointing to
664
  // the first of the newly inserted elements.
665
  iterator insert(const_iterator pos, std::initializer_list<value_type> list)
666
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
667
    return insert(pos, list.begin(), list.end());
668
  }
669
670
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
671
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
672
  // of the newly inserted elements.
673
  //
674
  // NOTE: this overload is for iterators that are "forward" category or better.
675
  template <typename ForwardIterator,
676
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
677
  iterator insert(const_iterator pos, ForwardIterator first,
678
                  ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
679
    ABSL_HARDENING_ASSERT(pos >= begin());
680
    ABSL_HARDENING_ASSERT(pos <= end());
681
682
    if (ABSL_PREDICT_TRUE(first != last)) {
683
      return storage_.Insert(
684
          pos, IteratorValueAdapter<A, ForwardIterator>(first),
685
          static_cast<size_type>(std::distance(first, last)));
686
    } else {
687
      return const_cast<iterator>(pos);
688
    }
689
  }
690
691
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
692
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
693
  // of the newly inserted elements.
694
  //
695
  // NOTE: this overload is for iterators that are "input" category.
696
  template <typename InputIterator,
697
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
698
  iterator insert(const_iterator pos, InputIterator first,
699
                  InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
700
    ABSL_HARDENING_ASSERT(pos >= begin());
701
    ABSL_HARDENING_ASSERT(pos <= end());
702
703
    size_type index = static_cast<size_type>(std::distance(cbegin(), pos));
704
    for (size_type i = index; first != last; ++i, static_cast<void>(++first)) {
705
      insert(data() + i, *first);
706
    }
707
708
    return iterator(data() + index);
709
  }
710
711
  // `InlinedVector::emplace(...)`
712
  //
713
  // Constructs and inserts an element using `args...` in the inlined vector at
714
  // `pos`, returning an `iterator` pointing to the newly emplaced element.
715
  template <typename... Args>
716
  iterator emplace(const_iterator pos,
717
                   Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
718
    ABSL_HARDENING_ASSERT(pos >= begin());
719
    ABSL_HARDENING_ASSERT(pos <= end());
720
721
    value_type dealias(std::forward<Args>(args)...);
722
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
723
    // It appears that GCC thinks that since `pos` is a const pointer and may
724
    // point to uninitialized memory at this point, a warning should be
725
    // issued. But `pos` is actually only used to compute an array index to
726
    // write to.
727
#if !defined(__clang__) && defined(__GNUC__)
728
#pragma GCC diagnostic push
729
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
730
#endif
731
    return storage_.Insert(pos,
732
                           IteratorValueAdapter<A, MoveIterator<A>>(
733
                               MoveIterator<A>(std::addressof(dealias))),
734
                           1);
735
#if !defined(__clang__) && defined(__GNUC__)
736
#pragma GCC diagnostic pop
737
#endif
738
  }
739
740
  // `InlinedVector::emplace_back(...)`
741
  //
742
  // Constructs and inserts an element using `args...` in the inlined vector at
743
  // `end()`, returning a `reference` to the newly emplaced element.
744
  template <typename... Args>
745
0
  reference emplace_back(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
746
0
    return storage_.EmplaceBack(std::forward<Args>(args)...);
747
0
  }
748
749
  // `InlinedVector::push_back(...)`
750
  //
751
  // Inserts a copy of `v` in the inlined vector at `end()`.
752
0
  void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
753
754
  // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
755
  // using move semantics.
756
  void push_back(value_type&& v) {
757
    static_cast<void>(emplace_back(std::move(v)));
758
  }
759
760
  // `InlinedVector::pop_back()`
761
  //
762
  // Destroys the element at `back()`, reducing the size by `1`.
763
  void pop_back() noexcept {
764
    ABSL_HARDENING_ASSERT(!empty());
765
766
    AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
767
    storage_.SubtractSize(1);
768
  }
769
770
  // `InlinedVector::erase(...)`
771
  //
772
  // Erases the element at `pos`, returning an `iterator` pointing to where the
773
  // erased element was located.
774
  //
775
  // NOTE: may return `end()`, which is not dereferenceable.
776
  iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND {
777
    ABSL_HARDENING_ASSERT(pos >= begin());
778
    ABSL_HARDENING_ASSERT(pos < end());
779
780
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
781
    // It appears that GCC thinks that since `pos` is a const pointer and may
782
    // point to uninitialized memory at this point, a warning should be
783
    // issued. But `pos` is actually only used to compute an array index to
784
    // write to.
785
#if !defined(__clang__) && defined(__GNUC__)
786
#pragma GCC diagnostic push
787
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
788
#pragma GCC diagnostic ignored "-Wuninitialized"
789
#endif
790
    return storage_.Erase(pos, pos + 1);
791
#if !defined(__clang__) && defined(__GNUC__)
792
#pragma GCC diagnostic pop
793
#endif
794
  }
795
796
  // Overload of `InlinedVector::erase(...)` that erases every element in the
797
  // range [`from`, `to`), returning an `iterator` pointing to where the first
798
  // erased element was located.
799
  //
800
  // NOTE: may return `end()`, which is not dereferenceable.
801
  iterator erase(const_iterator from,
802
                 const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND {
803
    ABSL_HARDENING_ASSERT(from >= begin());
804
    ABSL_HARDENING_ASSERT(from <= to);
805
    ABSL_HARDENING_ASSERT(to <= end());
806
807
    if (ABSL_PREDICT_TRUE(from != to)) {
808
      return storage_.Erase(from, to);
809
    } else {
810
      return const_cast<iterator>(from);
811
    }
812
  }
813
814
  // `InlinedVector::clear()`
815
  //
816
  // Destroys all elements in the inlined vector, setting the size to `0` and
817
  // preserving capacity.
818
0
  void clear() noexcept {
819
0
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
820
0
        storage_.GetAllocator(), data(), size());
821
0
    storage_.SetSize(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
  return H::combine_contiguous(std::move(h), a.data(), a.size());
1009
}
1010
1011
template <typename T, size_t N, typename A, typename Predicate>
1012
constexpr typename InlinedVector<T, N, A>::size_type erase_if(
1013
    InlinedVector<T, N, A>& v, Predicate pred) {
1014
  const auto it = std::remove_if(v.begin(), v.end(), std::move(pred));
1015
  const auto removed = static_cast<typename InlinedVector<T, N, A>::size_type>(
1016
      std::distance(it, v.end()));
1017
  v.erase(it, v.end());
1018
  return removed;
1019
}
1020
1021
ABSL_NAMESPACE_END
1022
}  // namespace absl
1023
1024
#endif  // ABSL_CONTAINER_INLINED_VECTOR_H_