Coverage Report

Created: 2025-07-11 06:37

/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/attributes.h"
50
#include "absl/base/internal/iterator_traits.h"
51
#include "absl/base/internal/throw_delegate.h"
52
#include "absl/base/macros.h"
53
#include "absl/base/optimization.h"
54
#include "absl/base/port.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
      base_internal::ThrowStdOutOfRange(
386
          "`InlinedVector::at(size_type)` failed bounds check");
387
    }
388
    return data()[i];
389
  }
390
391
  // Overload of `InlinedVector::at(...)` that returns a `const_reference` to
392
  // the `i`th element of the inlined vector.
393
  //
394
  // NOTE: if `i` is not within the required range of `InlinedVector::at(...)`,
395
  // in both debug and non-debug builds, `std::out_of_range` will be thrown.
396
  const_reference at(size_type i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
397
    if (ABSL_PREDICT_FALSE(i >= size())) {
398
      base_internal::ThrowStdOutOfRange(
399
          "`InlinedVector::at(size_type) const` failed bounds check");
400
    }
401
    return data()[i];
402
  }
403
404
  // `InlinedVector::front()`
405
  //
406
  // Returns a `reference` to the first element of the inlined vector.
407
  reference front() ABSL_ATTRIBUTE_LIFETIME_BOUND {
408
    ABSL_HARDENING_ASSERT(!empty());
409
    return data()[0];
410
  }
411
412
  // Overload of `InlinedVector::front()` that returns a `const_reference` to
413
  // the first element of the inlined vector.
414
  const_reference front() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
415
    ABSL_HARDENING_ASSERT(!empty());
416
    return data()[0];
417
  }
418
419
  // `InlinedVector::back()`
420
  //
421
  // Returns a `reference` to the last element of the inlined vector.
422
  reference back() ABSL_ATTRIBUTE_LIFETIME_BOUND {
423
    ABSL_HARDENING_ASSERT(!empty());
424
    return data()[size() - 1];
425
  }
426
427
  // Overload of `InlinedVector::back()` that returns a `const_reference` to the
428
  // last element of the inlined vector.
429
  const_reference back() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
430
    ABSL_HARDENING_ASSERT(!empty());
431
    return data()[size() - 1];
432
  }
433
434
  // `InlinedVector::begin()`
435
  //
436
  // Returns an `iterator` to the beginning of the inlined vector.
437
  iterator begin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND { return data(); }
438
439
  // Overload of `InlinedVector::begin()` that returns a `const_iterator` to
440
  // the beginning of the inlined vector.
441
  const_iterator begin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
442
    return data();
443
  }
444
445
  // `InlinedVector::end()`
446
  //
447
  // Returns an `iterator` to the end of the inlined vector.
448
  iterator end() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
449
    return data() + size();
450
  }
451
452
  // Overload of `InlinedVector::end()` that returns a `const_iterator` to the
453
  // end of the inlined vector.
454
  const_iterator end() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
455
    return data() + size();
456
  }
457
458
  // `InlinedVector::cbegin()`
459
  //
460
  // Returns a `const_iterator` to the beginning of the inlined vector.
461
  const_iterator cbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
462
    return begin();
463
  }
464
465
  // `InlinedVector::cend()`
466
  //
467
  // Returns a `const_iterator` to the end of the inlined vector.
468
  const_iterator cend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
469
    return end();
470
  }
471
472
  // `InlinedVector::rbegin()`
473
  //
474
  // Returns a `reverse_iterator` from the end of the inlined vector.
475
  reverse_iterator rbegin() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
476
    return reverse_iterator(end());
477
  }
478
479
  // Overload of `InlinedVector::rbegin()` that returns a
480
  // `const_reverse_iterator` from the end of the inlined vector.
481
  const_reverse_iterator rbegin() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
482
    return const_reverse_iterator(end());
483
  }
484
485
  // `InlinedVector::rend()`
486
  //
487
  // Returns a `reverse_iterator` from the beginning of the inlined vector.
488
  reverse_iterator rend() noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
489
    return reverse_iterator(begin());
490
  }
491
492
  // Overload of `InlinedVector::rend()` that returns a `const_reverse_iterator`
493
  // from the beginning of the inlined vector.
494
  const_reverse_iterator rend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
495
    return const_reverse_iterator(begin());
496
  }
497
498
  // `InlinedVector::crbegin()`
499
  //
500
  // Returns a `const_reverse_iterator` from the end of the inlined vector.
501
  const_reverse_iterator crbegin() const noexcept
502
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
503
    return rbegin();
504
  }
505
506
  // `InlinedVector::crend()`
507
  //
508
  // Returns a `const_reverse_iterator` from the beginning of the inlined
509
  // vector.
510
  const_reverse_iterator crend() const noexcept ABSL_ATTRIBUTE_LIFETIME_BOUND {
511
    return rend();
512
  }
513
514
  // `InlinedVector::get_allocator()`
515
  //
516
  // Returns a copy of the inlined vector's allocator.
517
  allocator_type get_allocator() const { return storage_.GetAllocator(); }
518
519
  // ---------------------------------------------------------------------------
520
  // InlinedVector Member Mutators
521
  // ---------------------------------------------------------------------------
522
523
  // `InlinedVector::operator=(...)`
524
  //
525
  // Replaces the elements of the inlined vector with copies of the elements of
526
  // `list`.
527
  InlinedVector& operator=(std::initializer_list<value_type> list) {
528
    assign(list.begin(), list.end());
529
530
    return *this;
531
  }
532
533
  // Overload of `InlinedVector::operator=(...)` that replaces the elements of
534
  // the inlined vector with copies of the elements of `other`.
535
  InlinedVector& operator=(const InlinedVector& other) {
536
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
537
      const_pointer other_data = other.data();
538
      assign(other_data, other_data + other.size());
539
    }
540
541
    return *this;
542
  }
543
544
  // Overload of `InlinedVector::operator=(...)` that moves the elements of
545
  // `other` into the inlined vector.
546
  //
547
  // NOTE: as a result of calling this overload, `other` is left in a valid but
548
  // unspecified state.
549
  InlinedVector& operator=(InlinedVector&& other) {
550
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
551
      MoveAssignment(MoveAssignmentPolicy{}, std::move(other));
552
    }
553
554
    return *this;
555
  }
556
557
  // `InlinedVector::assign(...)`
558
  //
559
  // Replaces the contents of the inlined vector with `n` copies of `v`.
560
  void assign(size_type n, const_reference v) {
561
    storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
562
  }
563
564
  // Overload of `InlinedVector::assign(...)` that replaces the contents of the
565
  // inlined vector with copies of the elements of `list`.
566
  void assign(std::initializer_list<value_type> list) {
567
    assign(list.begin(), list.end());
568
  }
569
570
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
571
  // inlined vector with the range [`first`, `last`).
572
  //
573
  // NOTE: this overload is for iterators that are "forward" category or better.
574
  template <typename ForwardIterator,
575
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
576
  void assign(ForwardIterator first, ForwardIterator last) {
577
    storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
578
                    static_cast<size_t>(std::distance(first, last)));
579
  }
580
581
  // Overload of `InlinedVector::assign(...)` to replace the contents of the
582
  // inlined vector with the range [`first`, `last`).
583
  //
584
  // NOTE: this overload is for iterators that are "input" category.
585
  template <typename InputIterator,
586
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
587
  void assign(InputIterator first, InputIterator last) {
588
    size_type i = 0;
589
    for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
590
      data()[i] = *first;
591
    }
592
593
    erase(data() + i, data() + size());
594
    std::copy(first, last, std::back_inserter(*this));
595
  }
596
597
  // `InlinedVector::resize(...)`
598
  //
599
  // Resizes the inlined vector to contain `n` elements.
600
  //
601
  // NOTE: If `n` is smaller than `size()`, extra elements are destroyed. If `n`
602
  // is larger than `size()`, new elements are value-initialized.
603
  void resize(size_type n) {
604
    ABSL_HARDENING_ASSERT(n <= max_size());
605
    storage_.Resize(DefaultValueAdapter<A>(), n);
606
  }
607
608
  // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
609
  // contain `n` elements.
610
  //
611
  // NOTE: if `n` is smaller than `size()`, extra elements are destroyed. If `n`
612
  // is larger than `size()`, new elements are copied-constructed from `v`.
613
  void resize(size_type n, const_reference v) {
614
    ABSL_HARDENING_ASSERT(n <= max_size());
615
    storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
616
  }
617
618
  // `InlinedVector::insert(...)`
619
  //
620
  // Inserts a copy of `v` at `pos`, returning an `iterator` to the newly
621
  // inserted element.
622
  iterator insert(const_iterator pos,
623
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
624
    return emplace(pos, v);
625
  }
626
627
  // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
628
  // move semantics, returning an `iterator` to the newly inserted element.
629
  iterator insert(const_iterator pos,
630
                  value_type&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
631
    return emplace(pos, std::move(v));
632
  }
633
634
  // Overload of `InlinedVector::insert(...)` that inserts `n` contiguous copies
635
  // of `v` starting at `pos`, returning an `iterator` pointing to the first of
636
  // the newly inserted elements.
637
  iterator insert(const_iterator pos, size_type n,
638
                  const_reference v) ABSL_ATTRIBUTE_LIFETIME_BOUND {
639
    ABSL_HARDENING_ASSERT(pos >= begin());
640
    ABSL_HARDENING_ASSERT(pos <= end());
641
642
    if (ABSL_PREDICT_TRUE(n != 0)) {
643
      value_type dealias = v;
644
      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
645
      // It appears that GCC thinks that since `pos` is a const pointer and may
646
      // point to uninitialized memory at this point, a warning should be
647
      // issued. But `pos` is actually only used to compute an array index to
648
      // write to.
649
#if !defined(__clang__) && defined(__GNUC__)
650
#pragma GCC diagnostic push
651
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
652
#endif
653
      return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
654
                             n);
655
#if !defined(__clang__) && defined(__GNUC__)
656
#pragma GCC diagnostic pop
657
#endif
658
    } else {
659
      return const_cast<iterator>(pos);
660
    }
661
  }
662
663
  // Overload of `InlinedVector::insert(...)` that inserts copies of the
664
  // elements of `list` starting at `pos`, returning an `iterator` pointing to
665
  // the first of the newly inserted elements.
666
  iterator insert(const_iterator pos, std::initializer_list<value_type> list)
667
      ABSL_ATTRIBUTE_LIFETIME_BOUND {
668
    return insert(pos, list.begin(), list.end());
669
  }
670
671
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
672
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
673
  // of the newly inserted elements.
674
  //
675
  // NOTE: this overload is for iterators that are "forward" category or better.
676
  template <typename ForwardIterator,
677
            EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
678
  iterator insert(const_iterator pos, ForwardIterator first,
679
                  ForwardIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
680
    ABSL_HARDENING_ASSERT(pos >= begin());
681
    ABSL_HARDENING_ASSERT(pos <= end());
682
683
    if (ABSL_PREDICT_TRUE(first != last)) {
684
      return storage_.Insert(
685
          pos, IteratorValueAdapter<A, ForwardIterator>(first),
686
          static_cast<size_type>(std::distance(first, last)));
687
    } else {
688
      return const_cast<iterator>(pos);
689
    }
690
  }
691
692
  // Overload of `InlinedVector::insert(...)` that inserts the range [`first`,
693
  // `last`) starting at `pos`, returning an `iterator` pointing to the first
694
  // of the newly inserted elements.
695
  //
696
  // NOTE: this overload is for iterators that are "input" category.
697
  template <typename InputIterator,
698
            DisableIfAtLeastForwardIterator<InputIterator> = 0>
699
  iterator insert(const_iterator pos, InputIterator first,
700
                  InputIterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
701
    ABSL_HARDENING_ASSERT(pos >= begin());
702
    ABSL_HARDENING_ASSERT(pos <= end());
703
704
    size_type index = static_cast<size_type>(std::distance(cbegin(), pos));
705
    for (size_type i = index; first != last; ++i, static_cast<void>(++first)) {
706
      insert(data() + i, *first);
707
    }
708
709
    return iterator(data() + index);
710
  }
711
712
  // `InlinedVector::emplace(...)`
713
  //
714
  // Constructs and inserts an element using `args...` in the inlined vector at
715
  // `pos`, returning an `iterator` pointing to the newly emplaced element.
716
  template <typename... Args>
717
  iterator emplace(const_iterator pos,
718
                   Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
719
    ABSL_HARDENING_ASSERT(pos >= begin());
720
    ABSL_HARDENING_ASSERT(pos <= end());
721
722
    value_type dealias(std::forward<Args>(args)...);
723
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
724
    // It appears that GCC thinks that since `pos` is a const pointer and may
725
    // point to uninitialized memory at this point, a warning should be
726
    // issued. But `pos` is actually only used to compute an array index to
727
    // write to.
728
#if !defined(__clang__) && defined(__GNUC__)
729
#pragma GCC diagnostic push
730
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
731
#endif
732
    return storage_.Insert(pos,
733
                           IteratorValueAdapter<A, MoveIterator<A>>(
734
                               MoveIterator<A>(std::addressof(dealias))),
735
                           1);
736
#if !defined(__clang__) && defined(__GNUC__)
737
#pragma GCC diagnostic pop
738
#endif
739
  }
740
741
  // `InlinedVector::emplace_back(...)`
742
  //
743
  // Constructs and inserts an element using `args...` in the inlined vector at
744
  // `end()`, returning a `reference` to the newly emplaced element.
745
  template <typename... Args>
746
0
  reference emplace_back(Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND {
747
0
    return storage_.EmplaceBack(std::forward<Args>(args)...);
748
0
  }
749
750
  // `InlinedVector::push_back(...)`
751
  //
752
  // Inserts a copy of `v` in the inlined vector at `end()`.
753
0
  void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
754
755
  // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
756
  // using move semantics.
757
  void push_back(value_type&& v) {
758
    static_cast<void>(emplace_back(std::move(v)));
759
  }
760
761
  // `InlinedVector::pop_back()`
762
  //
763
  // Destroys the element at `back()`, reducing the size by `1`.
764
  void pop_back() noexcept {
765
    ABSL_HARDENING_ASSERT(!empty());
766
767
    AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
768
    storage_.SubtractSize(1);
769
  }
770
771
  // `InlinedVector::erase(...)`
772
  //
773
  // Erases the element at `pos`, returning an `iterator` pointing to where the
774
  // erased element was located.
775
  //
776
  // NOTE: may return `end()`, which is not dereferenceable.
777
  iterator erase(const_iterator pos) ABSL_ATTRIBUTE_LIFETIME_BOUND {
778
    ABSL_HARDENING_ASSERT(pos >= begin());
779
    ABSL_HARDENING_ASSERT(pos < end());
780
781
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102329#c2
782
    // It appears that GCC thinks that since `pos` is a const pointer and may
783
    // point to uninitialized memory at this point, a warning should be
784
    // issued. But `pos` is actually only used to compute an array index to
785
    // write to.
786
#if !defined(__clang__) && defined(__GNUC__)
787
#pragma GCC diagnostic push
788
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
789
#pragma GCC diagnostic ignored "-Wuninitialized"
790
#endif
791
    return storage_.Erase(pos, pos + 1);
792
#if !defined(__clang__) && defined(__GNUC__)
793
#pragma GCC diagnostic pop
794
#endif
795
  }
796
797
  // Overload of `InlinedVector::erase(...)` that erases every element in the
798
  // range [`from`, `to`), returning an `iterator` pointing to where the first
799
  // erased element was located.
800
  //
801
  // NOTE: may return `end()`, which is not dereferenceable.
802
  iterator erase(const_iterator from,
803
                 const_iterator to) ABSL_ATTRIBUTE_LIFETIME_BOUND {
804
    ABSL_HARDENING_ASSERT(from >= begin());
805
    ABSL_HARDENING_ASSERT(from <= to);
806
    ABSL_HARDENING_ASSERT(to <= end());
807
808
    if (ABSL_PREDICT_TRUE(from != to)) {
809
      return storage_.Erase(from, to);
810
    } else {
811
      return const_cast<iterator>(from);
812
    }
813
  }
814
815
  // `InlinedVector::clear()`
816
  //
817
  // Destroys all elements in the inlined vector, setting the size to `0` and
818
  // deallocating any held memory.
819
0
  void clear() noexcept {
820
0
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
821
0
        storage_.GetAllocator(), data(), size());
822
0
    storage_.DeallocateIfAllocated();
823
824
0
    storage_.SetInlinedSize(0);
825
0
  }
826
827
  // `InlinedVector::reserve(...)`
828
  //
829
  // Ensures that there is enough room for at least `n` elements.
830
  void reserve(size_type n) { storage_.Reserve(n); }
831
832
  // `InlinedVector::shrink_to_fit()`
833
  //
834
  // Attempts to reduce memory usage by moving elements to (or keeping elements
835
  // in) the smallest available buffer sufficient for containing `size()`
836
  // elements.
837
  //
838
  // If `size()` is sufficiently small, the elements will be moved into (or kept
839
  // in) the inlined space.
840
  void shrink_to_fit() {
841
    if (storage_.GetIsAllocated()) {
842
      storage_.ShrinkToFit();
843
    }
844
  }
845
846
  // `InlinedVector::swap(...)`
847
  //
848
  // Swaps the contents of the inlined vector with `other`.
849
  void swap(InlinedVector& other) {
850
    if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
851
      storage_.Swap(std::addressof(other.storage_));
852
    }
853
  }
854
855
 private:
856
  template <typename H, typename TheT, size_t TheN, typename TheA>
857
  friend H AbslHashValue(H h, const absl::InlinedVector<TheT, TheN, TheA>& a);
858
859
  void MoveAssignment(MemcpyPolicy, InlinedVector&& other) {
860
    // Assumption check: we shouldn't be told to use memcpy to implement move
861
    // assignment unless we have trivially destructible elements and an
862
    // allocator that does nothing fancy.
863
    static_assert(absl::is_trivially_destructible<value_type>::value, "");
864
    static_assert(std::is_same<A, std::allocator<value_type>>::value, "");
865
866
    // Throw away our existing heap allocation, if any. There is no need to
867
    // destroy the existing elements one by one because we know they are
868
    // trivially destructible.
869
    storage_.DeallocateIfAllocated();
870
871
    // Adopt the other vector's inline elements or heap allocation.
872
    storage_.MemcpyFrom(other.storage_);
873
    other.storage_.SetInlinedSize(0);
874
  }
875
876
  // Destroy our existing elements, if any, and adopt the heap-allocated
877
  // elements of the other vector.
878
  //
879
  // REQUIRES: other.storage_.GetIsAllocated()
880
  void DestroyExistingAndAdopt(InlinedVector&& other) {
881
    ABSL_HARDENING_ASSERT(other.storage_.GetIsAllocated());
882
883
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
884
        storage_.GetAllocator(), data(), size());
885
    storage_.DeallocateIfAllocated();
886
887
    storage_.MemcpyFrom(other.storage_);
888
    other.storage_.SetInlinedSize(0);
889
  }
890
891
  void MoveAssignment(ElementwiseAssignPolicy, InlinedVector&& other) {
892
    // Fast path: if the other vector is on the heap then we don't worry about
893
    // actually move-assigning each element. Instead we only throw away our own
894
    // existing elements and adopt the heap allocation of the other vector.
895
    if (other.storage_.GetIsAllocated()) {
896
      DestroyExistingAndAdopt(std::move(other));
897
      return;
898
    }
899
900
    storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
901
                        MoveIterator<A>(other.storage_.GetInlinedData())),
902
                    other.size());
903
  }
904
905
  void MoveAssignment(ElementwiseConstructPolicy, InlinedVector&& other) {
906
    // Fast path: if the other vector is on the heap then we don't worry about
907
    // actually move-assigning each element. Instead we only throw away our own
908
    // existing elements and adopt the heap allocation of the other vector.
909
    if (other.storage_.GetIsAllocated()) {
910
      DestroyExistingAndAdopt(std::move(other));
911
      return;
912
    }
913
914
    inlined_vector_internal::DestroyAdapter<A>::DestroyElements(
915
        storage_.GetAllocator(), data(), size());
916
    storage_.DeallocateIfAllocated();
917
918
    IteratorValueAdapter<A, MoveIterator<A>> other_values(
919
        MoveIterator<A>(other.storage_.GetInlinedData()));
920
    inlined_vector_internal::ConstructElements<A>(
921
        storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
922
        other.storage_.GetSize());
923
    storage_.SetInlinedSize(other.storage_.GetSize());
924
  }
925
926
  Storage storage_;
927
};
928
929
// -----------------------------------------------------------------------------
930
// InlinedVector Non-Member Functions
931
// -----------------------------------------------------------------------------
932
933
// `swap(...)`
934
//
935
// Swaps the contents of two inlined vectors.
936
template <typename T, size_t N, typename A>
937
void swap(absl::InlinedVector<T, N, A>& a,
938
          absl::InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
939
  a.swap(b);
940
}
941
942
// `operator==(...)`
943
//
944
// Tests for value-equality of two inlined vectors.
945
template <typename T, size_t N, typename A>
946
bool operator==(const absl::InlinedVector<T, N, A>& a,
947
                const absl::InlinedVector<T, N, A>& b) {
948
  auto a_data = a.data();
949
  auto b_data = b.data();
950
  return std::equal(a_data, a_data + a.size(), b_data, b_data + b.size());
951
}
952
953
// `operator!=(...)`
954
//
955
// Tests for value-inequality of two inlined vectors.
956
template <typename T, size_t N, typename A>
957
bool operator!=(const absl::InlinedVector<T, N, A>& a,
958
                const absl::InlinedVector<T, N, A>& b) {
959
  return !(a == b);
960
}
961
962
// `operator<(...)`
963
//
964
// Tests whether the value of an inlined vector is less than the value of
965
// another inlined vector using a lexicographical comparison algorithm.
966
template <typename T, size_t N, typename A>
967
bool operator<(const absl::InlinedVector<T, N, A>& a,
968
               const absl::InlinedVector<T, N, A>& b) {
969
  auto a_data = a.data();
970
  auto b_data = b.data();
971
  return std::lexicographical_compare(a_data, a_data + a.size(), b_data,
972
                                      b_data + b.size());
973
}
974
975
// `operator>(...)`
976
//
977
// Tests whether the value of an inlined vector is greater than the value of
978
// another inlined vector using a lexicographical comparison algorithm.
979
template <typename T, size_t N, typename A>
980
bool operator>(const absl::InlinedVector<T, N, A>& a,
981
               const absl::InlinedVector<T, N, A>& b) {
982
  return b < a;
983
}
984
985
// `operator<=(...)`
986
//
987
// Tests whether the value of an inlined vector is less than or equal to the
988
// value of another inlined vector using a lexicographical comparison algorithm.
989
template <typename T, size_t N, typename A>
990
bool operator<=(const absl::InlinedVector<T, N, A>& a,
991
                const absl::InlinedVector<T, N, A>& b) {
992
  return !(b < a);
993
}
994
995
// `operator>=(...)`
996
//
997
// Tests whether the value of an inlined vector is greater than or equal to the
998
// value of another inlined vector using a lexicographical comparison algorithm.
999
template <typename T, size_t N, typename A>
1000
bool operator>=(const absl::InlinedVector<T, N, A>& a,
1001
                const absl::InlinedVector<T, N, A>& b) {
1002
  return !(a < b);
1003
}
1004
1005
// `AbslHashValue(...)`
1006
//
1007
// Provides `absl::Hash` support for `absl::InlinedVector`. It is uncommon to
1008
// call this directly.
1009
template <typename H, typename T, size_t N, typename A>
1010
H AbslHashValue(H h, const absl::InlinedVector<T, N, A>& a) {
1011
  return H::combine_contiguous(std::move(h), a.data(), a.size());
1012
}
1013
1014
ABSL_NAMESPACE_END
1015
}  // namespace absl
1016
1017
#endif  // ABSL_CONTAINER_INLINED_VECTOR_H_