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