284378a71b
-- 22fa219d17b2281c0695642830c4300711bd65ea by CJ Johnson <johnsoncj@google.com>: Rearrange the private method declarations in InlinedVector PiperOrigin-RevId: 224202447 -- eed3c9f488f23b521bee41d3683eb6cc22517ded by Derek Mauro <dmauro@google.com>: Fix leak_check target (it was always a no-op when LSAN isn't available). Fixes https://github.com/abseil/abseil-cpp/issues/232 PiperOrigin-RevId: 224201634 -- fc08039e175204b14a9561f618fcfc0234586801 by Greg Falcon <gfalcon@google.com>: Add parens around more invocations of min() and max() missed in my prior CL. PiperOrigin-RevId: 224162430 -- 0ec5476a8293c7796cd84928a1a558b14f14f222 by Abseil Team <absl-team@google.com>: Update absl/numeric/CMakeLists.txt to use new functions i.e. absl_cc_(library|test) PiperOrigin-RevId: 224139165 -- 2b46aa6fabb20c589661f8bbc84030ecf39ce394 by Abseil Team <absl-team@google.com>: Update absl/meta/CMakeLists.txt to use new functions i.e. absl_cc_(library|test) PiperOrigin-RevId: 224117258 -- 6c951c798f8c6903bd8793a8a4b5f69244be8aa9 by Abseil Team <absl-team@google.com>: Fix 2 Unused C++ BUILD Dependencies PiperOrigin-RevId: 224070093 -- 0ee7bd191708708f91fc5209c197fd93f6e4a8b3 by Greg Falcon <gfalcon@google.com>: Inside Abseil headers, wrap most invocations of methods and functions named `min` and `max` in parentheses, for better interoperability with Windows toolchains. CCTZ fixes will appear in a follow-up CL. PiperOrigin-RevId: 224051960 -- f562f56577b84a8bc07e5873775c01d068531bca by Jon Cohen <cohenjon@google.com>: Generate Abseil compile options. The single source of truth is now absl/copts/copts.py The way this works goes something like this: copts.py acts as the configuration file. We use python because unlike JSON it allows comments. It has two maps in it: one from names to external flags, and one from names to internal flags. generate_copts.py imports the maps and loops through them to write GENERATED_copts.bzl and GENERATED_AbseilCopts.cmake AbseilConfigureCopts.cmake and configure_copts.bzl import their respective copts args and set the platform-appropriate copts into ABSL_DEFAULT_COPTS, ABSL_TEST_COPTS, ABSL_EXCEPTIONS_FLAG, and ABSL_EXCEPTIONS_LINKOPTS For Bazel, each BUILD file load()s configure_copts.bzl For CMake, AbseilHelpers.cmake include()s AbseilConfigureCopts.cmake to get the final copts and both inserts them as needed into legacy target rules and also makes them available to the rest of our CMakeLists.txt file. We may instead want to include() AbseilConfigureCopts.cmake directly into each CMakeLists.txt file for consistency, but I'm not sure what the deal is with cmake and include guards, or if they are even needed. That's also not as idiomatic -- CMake tends to use directory scope where globals set at a higher level CMakeLists.txt file are used in the subdirectory CMakeLists.txt files. PiperOrigin-RevId: 224039419 -- f7402f6bb65037e668a7355f0a003f5c05a3b6a7 by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 224036622 GitOrigin-RevId: 22fa219d17b2281c0695642830c4300711bd65ea Change-Id: I6b505360539ff2aef8aa30c51a5f7d55db1c75cf
1447 lines
51 KiB
C++
1447 lines
51 KiB
C++
// Copyright 2018 The Abseil Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
// File: inlined_vector.h
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// This header file contains the declaration and definition of an "inlined
|
|
// vector" which behaves in an equivalent fashion to a `std::vector`, except
|
|
// that storage for small sequences of the vector are provided inline without
|
|
// requiring any heap allocation.
|
|
//
|
|
// An `absl::InlinedVector<T, N>` specifies the default capacity `N` as one of
|
|
// its template parameters. Instances where `size() <= N` hold contained
|
|
// elements in inline space. Typically `N` is very small so that sequences that
|
|
// are expected to be short do not require allocations.
|
|
//
|
|
// An `absl::InlinedVector` does not usually require a specific allocator. If
|
|
// the inlined vector grows beyond its initial constraints, it will need to
|
|
// allocate (as any normal `std::vector` would). This is usually performed with
|
|
// the default allocator (defined as `std::allocator<T>`). Optionally, a custom
|
|
// allocator type may be specified as `A` in `absl::InlinedVector<T, N, A>`.
|
|
|
|
#ifndef ABSL_CONTAINER_INLINED_VECTOR_H_
|
|
#define ABSL_CONTAINER_INLINED_VECTOR_H_
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "absl/algorithm/algorithm.h"
|
|
#include "absl/base/internal/throw_delegate.h"
|
|
#include "absl/base/optimization.h"
|
|
#include "absl/base/port.h"
|
|
#include "absl/memory/memory.h"
|
|
|
|
namespace absl {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// InlinedVector
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// An `absl::InlinedVector` is designed to be a drop-in replacement for
|
|
// `std::vector` for use cases where the vector's size is sufficiently small
|
|
// that it can be inlined. If the inlined vector does grow beyond its estimated
|
|
// capacity, it will trigger an initial allocation on the heap, and will behave
|
|
// as a `std:vector`. The API of the `absl::InlinedVector` within this file is
|
|
// designed to cover the same API footprint as covered by `std::vector`.
|
|
template <typename T, size_t N, typename A = std::allocator<T>>
|
|
class InlinedVector {
|
|
static_assert(N > 0, "InlinedVector requires inline capacity greater than 0");
|
|
constexpr static typename A::size_type inlined_capacity() {
|
|
return static_cast<typename A::size_type>(N);
|
|
}
|
|
|
|
template <typename Iterator>
|
|
using DisableIfIntegral =
|
|
absl::enable_if_t<!std::is_integral<Iterator>::value>;
|
|
|
|
template <typename Iterator>
|
|
using EnableIfInputIterator = absl::enable_if_t<std::is_convertible<
|
|
typename std::iterator_traits<Iterator>::iterator_category,
|
|
std::input_iterator_tag>::value>;
|
|
|
|
template <typename Iterator>
|
|
using IteratorCategory =
|
|
typename std::iterator_traits<Iterator>::iterator_category;
|
|
|
|
using rvalue_reference = typename A::value_type&&;
|
|
|
|
public:
|
|
using allocator_type = A;
|
|
using value_type = typename allocator_type::value_type;
|
|
using pointer = typename allocator_type::pointer;
|
|
using const_pointer = typename allocator_type::const_pointer;
|
|
using reference = typename allocator_type::reference;
|
|
using const_reference = typename allocator_type::const_reference;
|
|
using size_type = typename allocator_type::size_type;
|
|
using difference_type = typename allocator_type::difference_type;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// InlinedVector Constructors and Destructor
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Creates an empty inlined vector with a default initialized allocator.
|
|
InlinedVector() noexcept(noexcept(allocator_type()))
|
|
: allocator_and_tag_(allocator_type()) {}
|
|
|
|
// Creates an empty inlined vector with a specified allocator.
|
|
explicit InlinedVector(const allocator_type& alloc) noexcept
|
|
: allocator_and_tag_(alloc) {}
|
|
|
|
// Creates an inlined vector with `n` copies of `value_type()`.
|
|
explicit InlinedVector(size_type n,
|
|
const allocator_type& alloc = allocator_type())
|
|
: allocator_and_tag_(alloc) {
|
|
InitAssign(n);
|
|
}
|
|
|
|
// Creates an inlined vector with `n` copies of `v`.
|
|
InlinedVector(size_type n, const_reference v,
|
|
const allocator_type& alloc = allocator_type())
|
|
: allocator_and_tag_(alloc) {
|
|
InitAssign(n, v);
|
|
}
|
|
|
|
// Creates an inlined vector of copies of the values in `init_list`.
|
|
InlinedVector(std::initializer_list<value_type> init_list,
|
|
const allocator_type& alloc = allocator_type())
|
|
: allocator_and_tag_(alloc) {
|
|
AppendRange(init_list.begin(), init_list.end(),
|
|
IteratorCategory<decltype(init_list.begin())>{});
|
|
}
|
|
|
|
// Creates an inlined vector with elements constructed from the provided
|
|
// Iterator range [`first`, `last`).
|
|
//
|
|
// NOTE: The `enable_if` prevents ambiguous interpretation between a call to
|
|
// this constructor with two integral arguments and a call to the above
|
|
// `InlinedVector(size_type, const_reference)` constructor.
|
|
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
|
|
InlinedVector(InputIterator first, InputIterator last,
|
|
const allocator_type& alloc = allocator_type())
|
|
: allocator_and_tag_(alloc) {
|
|
AppendRange(first, last, IteratorCategory<InputIterator>{});
|
|
}
|
|
|
|
// Creates a copy of `other` using `other`'s allocator.
|
|
InlinedVector(const InlinedVector& other);
|
|
|
|
// Creates a copy of `other` but with a specified allocator.
|
|
InlinedVector(const InlinedVector& other, const allocator_type& alloc);
|
|
|
|
// Creates an inlined vector by moving in the contents of `other`.
|
|
//
|
|
// NOTE: This move constructor does not allocate and only moves the underlying
|
|
// objects, so its `noexcept` specification depends on whether moving the
|
|
// underlying objects can throw or not. We assume:
|
|
// a) move constructors should only throw due to allocation failure and
|
|
// b) if `value_type`'s move constructor allocates, it uses the same
|
|
// allocation function as the `InlinedVector`'s allocator, so the move
|
|
// constructor is non-throwing if the allocator is non-throwing or
|
|
// `value_type`'s move constructor is specified as `noexcept`.
|
|
InlinedVector(InlinedVector&& v) noexcept(
|
|
absl::allocator_is_nothrow<allocator_type>::value ||
|
|
std::is_nothrow_move_constructible<value_type>::value);
|
|
|
|
// Creates an inlined vector by moving in the contents of `other`.
|
|
//
|
|
// NOTE: This move constructor allocates and subsequently moves the underlying
|
|
// objects, so its `noexcept` specification depends on whether the allocation
|
|
// can throw and whether moving the underlying objects can throw. Based on the
|
|
// same assumptions as above, the `noexcept` specification is dominated by
|
|
// whether the allocation can throw regardless of whether `value_type`'s move
|
|
// constructor is specified as `noexcept`.
|
|
InlinedVector(InlinedVector&& v, const allocator_type& alloc) noexcept(
|
|
absl::allocator_is_nothrow<allocator_type>::value);
|
|
|
|
~InlinedVector() { clear(); }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// InlinedVector Member Accessors
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// `InlinedVector::empty()`
|
|
//
|
|
// Checks if the inlined vector has no elements.
|
|
bool empty() const noexcept { return !size(); }
|
|
|
|
// `InlinedVector::size()`
|
|
//
|
|
// Returns the number of elements in the inlined vector.
|
|
size_type size() const noexcept { return tag().size(); }
|
|
|
|
// `InlinedVector::max_size()`
|
|
//
|
|
// Returns the maximum number of elements the vector can hold.
|
|
size_type max_size() const noexcept {
|
|
// One bit of the size storage is used to indicate whether the inlined
|
|
// vector is allocated. As a result, the maximum size of the container that
|
|
// we can express is half of the max for `size_type`.
|
|
return (std::numeric_limits<size_type>::max)() / 2;
|
|
}
|
|
|
|
// `InlinedVector::capacity()`
|
|
//
|
|
// Returns the number of elements that can be stored in the inlined vector
|
|
// without requiring a reallocation of underlying memory.
|
|
//
|
|
// NOTE: For most inlined vectors, `capacity()` should equal
|
|
// `inlined_capacity()`. For inlined vectors which exceed this capacity, they
|
|
// will no longer be inlined and `capacity()` will equal its capacity on the
|
|
// allocated heap.
|
|
size_type capacity() const noexcept {
|
|
return allocated() ? allocation().capacity() : inlined_capacity();
|
|
}
|
|
|
|
// `InlinedVector::data()`
|
|
//
|
|
// Returns a `pointer` to elements of the inlined vector. This pointer can be
|
|
// used to access and modify the contained elements.
|
|
// Only results within the range [`0`, `size()`) are defined.
|
|
pointer data() noexcept {
|
|
return allocated() ? allocated_space() : inlined_space();
|
|
}
|
|
|
|
// Overload of `InlinedVector::data()` to return a `const_pointer` to elements
|
|
// of the inlined vector. This pointer can be used to access (but not modify)
|
|
// the contained elements.
|
|
const_pointer data() const noexcept {
|
|
return allocated() ? allocated_space() : inlined_space();
|
|
}
|
|
|
|
// `InlinedVector::operator[]()`
|
|
//
|
|
// Returns a `reference` to the `i`th element of the inlined vector using the
|
|
// array operator.
|
|
reference operator[](size_type i) {
|
|
assert(i < size());
|
|
return data()[i];
|
|
}
|
|
|
|
// Overload of `InlinedVector::operator[]()` to return a `const_reference` to
|
|
// the `i`th element of the inlined vector.
|
|
const_reference operator[](size_type i) const {
|
|
assert(i < size());
|
|
return data()[i];
|
|
}
|
|
|
|
// `InlinedVector::at()`
|
|
//
|
|
// Returns a `reference` to the `i`th element of the inlined vector.
|
|
reference at(size_type i) {
|
|
if (ABSL_PREDICT_FALSE(i >= size())) {
|
|
base_internal::ThrowStdOutOfRange(
|
|
"InlinedVector::at() failed bounds check");
|
|
}
|
|
return data()[i];
|
|
}
|
|
|
|
// Overload of `InlinedVector::at()` to return a `const_reference` to the
|
|
// `i`th element of the inlined vector.
|
|
const_reference at(size_type i) const {
|
|
if (ABSL_PREDICT_FALSE(i >= size())) {
|
|
base_internal::ThrowStdOutOfRange(
|
|
"InlinedVector::at() failed bounds check");
|
|
}
|
|
return data()[i];
|
|
}
|
|
|
|
// `InlinedVector::front()`
|
|
//
|
|
// Returns a `reference` to the first element of the inlined vector.
|
|
reference front() {
|
|
assert(!empty());
|
|
return at(0);
|
|
}
|
|
|
|
// Overload of `InlinedVector::front()` returns a `const_reference` to the
|
|
// first element of the inlined vector.
|
|
const_reference front() const {
|
|
assert(!empty());
|
|
return at(0);
|
|
}
|
|
|
|
// `InlinedVector::back()`
|
|
//
|
|
// Returns a `reference` to the last element of the inlined vector.
|
|
reference back() {
|
|
assert(!empty());
|
|
return at(size() - 1);
|
|
}
|
|
|
|
// Overload of `InlinedVector::back()` to return a `const_reference` to the
|
|
// last element of the inlined vector.
|
|
const_reference back() const {
|
|
assert(!empty());
|
|
return at(size() - 1);
|
|
}
|
|
|
|
// `InlinedVector::begin()`
|
|
//
|
|
// Returns an `iterator` to the beginning of the inlined vector.
|
|
iterator begin() noexcept { return data(); }
|
|
|
|
// Overload of `InlinedVector::begin()` to return a `const_iterator` to
|
|
// the beginning of the inlined vector.
|
|
const_iterator begin() const noexcept { return data(); }
|
|
|
|
// `InlinedVector::end()`
|
|
//
|
|
// Returns an `iterator` to the end of the inlined vector.
|
|
iterator end() noexcept { return data() + size(); }
|
|
|
|
// Overload of `InlinedVector::end()` to return a `const_iterator` to the
|
|
// end of the inlined vector.
|
|
const_iterator end() const noexcept { return data() + size(); }
|
|
|
|
// `InlinedVector::cbegin()`
|
|
//
|
|
// Returns a `const_iterator` to the beginning of the inlined vector.
|
|
const_iterator cbegin() const noexcept { return begin(); }
|
|
|
|
// `InlinedVector::cend()`
|
|
//
|
|
// Returns a `const_iterator` to the end of the inlined vector.
|
|
const_iterator cend() const noexcept { return end(); }
|
|
|
|
// `InlinedVector::rbegin()`
|
|
//
|
|
// Returns a `reverse_iterator` from the end of the inlined vector.
|
|
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
|
|
|
// Overload of `InlinedVector::rbegin()` to return a
|
|
// `const_reverse_iterator` from the end of the inlined vector.
|
|
const_reverse_iterator rbegin() const noexcept {
|
|
return const_reverse_iterator(end());
|
|
}
|
|
|
|
// `InlinedVector::rend()`
|
|
//
|
|
// Returns a `reverse_iterator` from the beginning of the inlined vector.
|
|
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
|
|
|
// Overload of `InlinedVector::rend()` to return a `const_reverse_iterator`
|
|
// from the beginning of the inlined vector.
|
|
const_reverse_iterator rend() const noexcept {
|
|
return const_reverse_iterator(begin());
|
|
}
|
|
|
|
// `InlinedVector::crbegin()`
|
|
//
|
|
// Returns a `const_reverse_iterator` from the end of the inlined vector.
|
|
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
|
|
|
// `InlinedVector::crend()`
|
|
//
|
|
// Returns a `const_reverse_iterator` from the beginning of the inlined
|
|
// vector.
|
|
const_reverse_iterator crend() const noexcept { return rend(); }
|
|
|
|
// `InlinedVector::get_allocator()`
|
|
//
|
|
// Returns a copy of the allocator of the inlined vector.
|
|
allocator_type get_allocator() const { return allocator(); }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// InlinedVector Member Mutators
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// `InlinedVector::operator=()`
|
|
//
|
|
// Replaces the contents of the inlined vector with copies of the elements in
|
|
// the provided `std::initializer_list`.
|
|
InlinedVector& operator=(std::initializer_list<value_type> init_list) {
|
|
AssignRange(init_list.begin(), init_list.end(),
|
|
IteratorCategory<decltype(init_list.begin())>{});
|
|
return *this;
|
|
}
|
|
|
|
// Overload of `InlinedVector::operator=()` to replace the contents of the
|
|
// inlined vector with the contents of `other`.
|
|
InlinedVector& operator=(const InlinedVector& other) {
|
|
if (ABSL_PREDICT_FALSE(this == &other)) return *this;
|
|
|
|
// Optimized to avoid reallocation.
|
|
// Prefer reassignment to copy construction for elements.
|
|
if (size() < other.size()) { // grow
|
|
reserve(other.size());
|
|
std::copy(other.begin(), other.begin() + size(), begin());
|
|
std::copy(other.begin() + size(), other.end(), std::back_inserter(*this));
|
|
} else { // maybe shrink
|
|
erase(begin() + other.size(), end());
|
|
std::copy(other.begin(), other.end(), begin());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Overload of `InlinedVector::operator=()` to replace the contents of the
|
|
// inlined vector with the contents of `other`.
|
|
//
|
|
// NOTE: As a result of calling this overload, `other` may be empty or it's
|
|
// contents may be left in a moved-from state.
|
|
InlinedVector& operator=(InlinedVector&& other) {
|
|
if (ABSL_PREDICT_FALSE(this == &other)) return *this;
|
|
|
|
if (other.allocated()) {
|
|
clear();
|
|
tag().set_allocated_size(other.size());
|
|
init_allocation(other.allocation());
|
|
other.tag() = Tag();
|
|
} else {
|
|
if (allocated()) clear();
|
|
// Both are inlined now.
|
|
if (size() < other.size()) {
|
|
auto mid = std::make_move_iterator(other.begin() + size());
|
|
std::copy(std::make_move_iterator(other.begin()), mid, begin());
|
|
UninitializedCopy(mid, std::make_move_iterator(other.end()), end());
|
|
} else {
|
|
auto new_end = std::copy(std::make_move_iterator(other.begin()),
|
|
std::make_move_iterator(other.end()), begin());
|
|
Destroy(new_end, end());
|
|
}
|
|
tag().set_inline_size(other.size());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// `InlinedVector::assign()`
|
|
//
|
|
// Replaces the contents of the inlined vector with `n` copies of `v`.
|
|
void assign(size_type n, const_reference v) {
|
|
if (n <= size()) { // Possibly shrink
|
|
std::fill_n(begin(), n, v);
|
|
erase(begin() + n, end());
|
|
return;
|
|
}
|
|
// Grow
|
|
reserve(n);
|
|
std::fill_n(begin(), size(), v);
|
|
if (allocated()) {
|
|
UninitializedFill(allocated_space() + size(), allocated_space() + n, v);
|
|
tag().set_allocated_size(n);
|
|
} else {
|
|
UninitializedFill(inlined_space() + size(), inlined_space() + n, v);
|
|
tag().set_inline_size(n);
|
|
}
|
|
}
|
|
|
|
// Overload of `InlinedVector::assign()` to replace the contents of the
|
|
// inlined vector with copies of the values in the provided
|
|
// `std::initializer_list`.
|
|
void assign(std::initializer_list<value_type> init_list) {
|
|
AssignRange(init_list.begin(), init_list.end(),
|
|
IteratorCategory<decltype(init_list.begin())>{});
|
|
}
|
|
|
|
// Overload of `InlinedVector::assign()` to replace the contents of the
|
|
// inlined vector with values constructed from the range [`first`, `last`).
|
|
template <typename InputIterator, DisableIfIntegral<InputIterator>* = nullptr>
|
|
void assign(InputIterator first, InputIterator last) {
|
|
AssignRange(first, last, IteratorCategory<InputIterator>{});
|
|
}
|
|
|
|
// `InlinedVector::resize()`
|
|
//
|
|
// Resizes the inlined vector to contain `n` elements. If `n` is smaller than
|
|
// the inlined vector's current size, extra elements are destroyed. If `n` is
|
|
// larger than the initial size, new elements are value-initialized.
|
|
void resize(size_type n);
|
|
|
|
// Overload of `InlinedVector::resize()` to resize the inlined vector to
|
|
// contain `n` elements where, if `n` is larger than `size()`, the new values
|
|
// will be copy-constructed from `v`.
|
|
void resize(size_type n, const_reference v);
|
|
|
|
// `InlinedVector::insert()`
|
|
//
|
|
// Copies `v` into `position`, returning an `iterator` pointing to the newly
|
|
// inserted element.
|
|
iterator insert(const_iterator position, const_reference v) {
|
|
return emplace(position, v);
|
|
}
|
|
|
|
// Overload of `InlinedVector::insert()` for moving `v` into `position`,
|
|
// returning an iterator pointing to the newly inserted element.
|
|
iterator insert(const_iterator position, rvalue_reference v) {
|
|
return emplace(position, std::move(v));
|
|
}
|
|
|
|
// Overload of `InlinedVector::insert()` for inserting `n` contiguous copies
|
|
// of `v` starting at `position`. Returns an `iterator` pointing to the first
|
|
// of the newly inserted elements.
|
|
iterator insert(const_iterator position, size_type n, const_reference v) {
|
|
return InsertWithCount(position, n, v);
|
|
}
|
|
|
|
// Overload of `InlinedVector::insert()` for copying the contents of the
|
|
// `std::initializer_list` into the vector starting at `position`. Returns an
|
|
// `iterator` pointing to the first of the newly inserted elements.
|
|
iterator insert(const_iterator position,
|
|
std::initializer_list<value_type> init_list) {
|
|
return insert(position, init_list.begin(), init_list.end());
|
|
}
|
|
|
|
// Overload of `InlinedVector::insert()` for inserting elements constructed
|
|
// from the range [`first`, `last`). Returns an `iterator` pointing to the
|
|
// first of the newly inserted elements.
|
|
//
|
|
// NOTE: The `enable_if` is intended to disambiguate the two three-argument
|
|
// overloads of `insert()`.
|
|
template <typename InputIterator,
|
|
typename = EnableIfInputIterator<InputIterator>>
|
|
iterator insert(const_iterator position, InputIterator first,
|
|
InputIterator last) {
|
|
return InsertWithRange(position, first, last,
|
|
IteratorCategory<InputIterator>());
|
|
}
|
|
|
|
// `InlinedVector::emplace()`
|
|
//
|
|
// Constructs and inserts an object in the inlined vector at the given
|
|
// `position`, returning an `iterator` pointing to the newly emplaced element.
|
|
template <typename... Args>
|
|
iterator emplace(const_iterator position, Args&&... args);
|
|
|
|
// `InlinedVector::emplace_back()`
|
|
//
|
|
// Constructs and appends a new element to the end of the inlined vector,
|
|
// returning a `reference` to the emplaced element.
|
|
template <typename... Args>
|
|
reference emplace_back(Args&&... args) {
|
|
size_type s = size();
|
|
assert(s <= capacity());
|
|
if (ABSL_PREDICT_FALSE(s == capacity())) {
|
|
return GrowAndEmplaceBack(std::forward<Args>(args)...);
|
|
}
|
|
assert(s < capacity());
|
|
|
|
pointer space;
|
|
if (allocated()) {
|
|
tag().set_allocated_size(s + 1);
|
|
space = allocated_space();
|
|
} else {
|
|
tag().set_inline_size(s + 1);
|
|
space = inlined_space();
|
|
}
|
|
return Construct(space + s, std::forward<Args>(args)...);
|
|
}
|
|
|
|
// `InlinedVector::push_back()`
|
|
//
|
|
// Appends a copy of `v` to the end of the inlined vector.
|
|
void push_back(const_reference v) { static_cast<void>(emplace_back(v)); }
|
|
|
|
// Overload of `InlinedVector::push_back()` for moving `v` into a newly
|
|
// appended element.
|
|
void push_back(rvalue_reference v) {
|
|
static_cast<void>(emplace_back(std::move(v)));
|
|
}
|
|
|
|
// `InlinedVector::pop_back()`
|
|
//
|
|
// Destroys the element at the end of the inlined vector and shrinks the size
|
|
// by `1` (unless the inlined vector is empty, in which case this is a no-op).
|
|
void pop_back() noexcept {
|
|
assert(!empty());
|
|
size_type s = size();
|
|
if (allocated()) {
|
|
Destroy(allocated_space() + s - 1, allocated_space() + s);
|
|
tag().set_allocated_size(s - 1);
|
|
} else {
|
|
Destroy(inlined_space() + s - 1, inlined_space() + s);
|
|
tag().set_inline_size(s - 1);
|
|
}
|
|
}
|
|
|
|
// `InlinedVector::erase()`
|
|
//
|
|
// Erases the element at `position` of the inlined vector, returning an
|
|
// `iterator` pointing to the first element following the erased element.
|
|
//
|
|
// NOTE: May return the end iterator, which is not dereferencable.
|
|
iterator erase(const_iterator position) {
|
|
assert(position >= begin());
|
|
assert(position < end());
|
|
|
|
iterator pos = const_cast<iterator>(position);
|
|
std::move(pos + 1, end(), pos);
|
|
pop_back();
|
|
return pos;
|
|
}
|
|
|
|
// Overload of `InlinedVector::erase()` for erasing all elements in the
|
|
// range [`from`, `to`) in the inlined vector. Returns an `iterator` pointing
|
|
// to the first element following the range erased or the end iterator if `to`
|
|
// was the end iterator.
|
|
iterator erase(const_iterator from, const_iterator to);
|
|
|
|
// `InlinedVector::clear()`
|
|
//
|
|
// Destroys all elements in the inlined vector, sets the size of `0` and
|
|
// deallocates the heap allocation if the inlined vector was allocated.
|
|
void clear() noexcept {
|
|
size_type s = size();
|
|
if (allocated()) {
|
|
Destroy(allocated_space(), allocated_space() + s);
|
|
allocation().Dealloc(allocator());
|
|
} else if (s != 0) { // do nothing for empty vectors
|
|
Destroy(inlined_space(), inlined_space() + s);
|
|
}
|
|
tag() = Tag();
|
|
}
|
|
|
|
// `InlinedVector::reserve()`
|
|
//
|
|
// Enlarges the underlying representation of the inlined vector so it can hold
|
|
// at least `n` elements. This method does not change `size()` or the actual
|
|
// contents of the vector.
|
|
//
|
|
// NOTE: If `n` does not exceed `capacity()`, `reserve()` will have no
|
|
// effects. Otherwise, `reserve()` will reallocate, performing an n-time
|
|
// element-wise move of everything contained.
|
|
void reserve(size_type n) {
|
|
if (n > capacity()) {
|
|
// Make room for new elements
|
|
EnlargeBy(n - size());
|
|
}
|
|
}
|
|
|
|
// `InlinedVector::shrink_to_fit()`
|
|
//
|
|
// Reduces memory usage by freeing unused memory. After this call, calls to
|
|
// `capacity()` will be equal to `(std::max)(inlined_capacity(), size())`.
|
|
//
|
|
// If `size() <= inlined_capacity()` and the elements are currently stored on
|
|
// the heap, they will be moved to the inlined storage and the heap memory
|
|
// will be deallocated.
|
|
//
|
|
// If `size() > inlined_capacity()` and `size() < capacity()` the elements
|
|
// will be moved to a smaller heap allocation.
|
|
void shrink_to_fit() {
|
|
const auto s = size();
|
|
if (ABSL_PREDICT_FALSE(!allocated() || s == capacity())) return;
|
|
|
|
if (s <= inlined_capacity()) {
|
|
// Move the elements to the inlined storage.
|
|
// We have to do this using a temporary, because `inlined_storage` and
|
|
// `allocation_storage` are in a union field.
|
|
auto temp = std::move(*this);
|
|
assign(std::make_move_iterator(temp.begin()),
|
|
std::make_move_iterator(temp.end()));
|
|
return;
|
|
}
|
|
|
|
// Reallocate storage and move elements.
|
|
// We can't simply use the same approach as above, because `assign()` would
|
|
// call into `reserve()` internally and reserve larger capacity than we need
|
|
Allocation new_allocation(allocator(), s);
|
|
UninitializedCopy(std::make_move_iterator(allocated_space()),
|
|
std::make_move_iterator(allocated_space() + s),
|
|
new_allocation.buffer());
|
|
ResetAllocation(new_allocation, s);
|
|
}
|
|
|
|
// `InlinedVector::swap()`
|
|
//
|
|
// Swaps the contents of this inlined vector with the contents of `other`.
|
|
void swap(InlinedVector& other);
|
|
|
|
template <typename Hash>
|
|
friend Hash AbslHashValue(Hash hash, const InlinedVector& inlined_vector) {
|
|
const_pointer p = inlined_vector.data();
|
|
size_type n = inlined_vector.size();
|
|
return Hash::combine(Hash::combine_contiguous(std::move(hash), p, n), n);
|
|
}
|
|
|
|
private:
|
|
// Holds whether the vector is allocated or not in the lowest bit and the size
|
|
// in the high bits:
|
|
// `size_ = (size << 1) | is_allocated;`
|
|
class Tag {
|
|
public:
|
|
Tag() : size_(0) {}
|
|
size_type size() const { return size_ / 2; }
|
|
void add_size(size_type n) { size_ += n * 2; }
|
|
void set_inline_size(size_type n) { size_ = n * 2; }
|
|
void set_allocated_size(size_type n) { size_ = (n * 2) + 1; }
|
|
bool allocated() const { return size_ % 2; }
|
|
|
|
private:
|
|
size_type size_;
|
|
};
|
|
|
|
// Derives from `allocator_type` to use the empty base class optimization.
|
|
// If the `allocator_type` is stateless, we can store our instance for free.
|
|
class AllocatorAndTag : private allocator_type {
|
|
public:
|
|
explicit AllocatorAndTag(const allocator_type& a) : allocator_type(a) {}
|
|
|
|
Tag& tag() { return tag_; }
|
|
const Tag& tag() const { return tag_; }
|
|
|
|
allocator_type& allocator() { return *this; }
|
|
const allocator_type& allocator() const { return *this; }
|
|
|
|
private:
|
|
Tag tag_;
|
|
};
|
|
|
|
class Allocation {
|
|
public:
|
|
Allocation(allocator_type& a, size_type capacity)
|
|
: capacity_(capacity), buffer_(Create(a, capacity)) {}
|
|
|
|
void Dealloc(allocator_type& a) {
|
|
std::allocator_traits<allocator_type>::deallocate(a, buffer_, capacity_);
|
|
}
|
|
|
|
size_type capacity() const { return capacity_; }
|
|
|
|
const_pointer buffer() const { return buffer_; }
|
|
|
|
pointer buffer() { return buffer_; }
|
|
|
|
private:
|
|
static pointer Create(allocator_type& a, size_type n) {
|
|
return std::allocator_traits<allocator_type>::allocate(a, n);
|
|
}
|
|
|
|
size_type capacity_;
|
|
pointer buffer_;
|
|
};
|
|
|
|
const Tag& tag() const { return allocator_and_tag_.tag(); }
|
|
|
|
Tag& tag() { return allocator_and_tag_.tag(); }
|
|
|
|
Allocation& allocation() {
|
|
return reinterpret_cast<Allocation&>(rep_.allocation_storage.allocation);
|
|
}
|
|
|
|
const Allocation& allocation() const {
|
|
return reinterpret_cast<const Allocation&>(
|
|
rep_.allocation_storage.allocation);
|
|
}
|
|
|
|
void init_allocation(const Allocation& allocation) {
|
|
new (&rep_.allocation_storage.allocation) Allocation(allocation);
|
|
}
|
|
|
|
// TODO(absl-team): investigate whether the reinterpret_cast is appropriate.
|
|
pointer inlined_space() {
|
|
return reinterpret_cast<pointer>(
|
|
std::addressof(rep_.inlined_storage.inlined[0]));
|
|
}
|
|
|
|
const_pointer inlined_space() const {
|
|
return reinterpret_cast<const_pointer>(
|
|
std::addressof(rep_.inlined_storage.inlined[0]));
|
|
}
|
|
|
|
pointer allocated_space() { return allocation().buffer(); }
|
|
|
|
const_pointer allocated_space() const { return allocation().buffer(); }
|
|
|
|
const allocator_type& allocator() const {
|
|
return allocator_and_tag_.allocator();
|
|
}
|
|
|
|
allocator_type& allocator() { return allocator_and_tag_.allocator(); }
|
|
|
|
bool allocated() const { return tag().allocated(); }
|
|
|
|
void ResetAllocation(Allocation new_allocation, size_type new_size) {
|
|
if (allocated()) {
|
|
Destroy(allocated_space(), allocated_space() + size());
|
|
assert(begin() == allocated_space());
|
|
allocation().Dealloc(allocator());
|
|
allocation() = new_allocation;
|
|
} else {
|
|
Destroy(inlined_space(), inlined_space() + size());
|
|
init_allocation(new_allocation); // bug: only init once
|
|
}
|
|
tag().set_allocated_size(new_size);
|
|
}
|
|
|
|
template <typename... Args>
|
|
reference Construct(pointer p, Args&&... args) {
|
|
std::allocator_traits<allocator_type>::construct(
|
|
allocator(), p, std::forward<Args>(args)...);
|
|
return *p;
|
|
}
|
|
|
|
template <typename Iterator>
|
|
void UninitializedCopy(Iterator src, Iterator src_last, pointer dst) {
|
|
for (; src != src_last; ++dst, ++src) Construct(dst, *src);
|
|
}
|
|
|
|
template <typename... Args>
|
|
void UninitializedFill(pointer dst, pointer dst_last, const Args&... args) {
|
|
for (; dst != dst_last; ++dst) Construct(dst, args...);
|
|
}
|
|
|
|
// Destroy [`from`, `to`) in place.
|
|
void Destroy(pointer from, pointer to);
|
|
|
|
// Enlarge the underlying representation so we can store `size_ + delta` elems
|
|
// in allocated space. The size is not changed, and any newly added memory is
|
|
// not initialized.
|
|
void EnlargeBy(size_type delta);
|
|
|
|
// Shift all elements from `position` to `end()` by `n` places to the right.
|
|
// If the vector needs to be enlarged, memory will be allocated.
|
|
// Returns `iterator`s pointing to the start of the previously-initialized
|
|
// portion and the start of the uninitialized portion of the created gap.
|
|
// The number of initialized spots is `pair.second - pair.first`. The number
|
|
// of raw spots is `n - (pair.second - pair.first)`.
|
|
//
|
|
// Updates the size of the InlinedVector internally.
|
|
std::pair<iterator, iterator> ShiftRight(const_iterator position,
|
|
size_type n);
|
|
|
|
template <typename... Args>
|
|
reference GrowAndEmplaceBack(Args&&... args) {
|
|
assert(size() == capacity());
|
|
const size_type s = size();
|
|
|
|
Allocation new_allocation(allocator(), 2 * capacity());
|
|
|
|
reference new_element =
|
|
Construct(new_allocation.buffer() + s, std::forward<Args>(args)...);
|
|
UninitializedCopy(std::make_move_iterator(data()),
|
|
std::make_move_iterator(data() + s),
|
|
new_allocation.buffer());
|
|
|
|
ResetAllocation(new_allocation, s + 1);
|
|
|
|
return new_element;
|
|
}
|
|
|
|
void InitAssign(size_type n);
|
|
|
|
void InitAssign(size_type n, const_reference v);
|
|
|
|
template <typename Iterator>
|
|
void AssignRange(Iterator first, Iterator last, std::forward_iterator_tag);
|
|
|
|
template <typename Iterator>
|
|
void AssignRange(Iterator first, Iterator last, std::input_iterator_tag);
|
|
|
|
template <typename Iterator>
|
|
void AppendRange(Iterator first, Iterator last, std::forward_iterator_tag);
|
|
|
|
template <typename Iterator>
|
|
void AppendRange(Iterator first, Iterator last, std::input_iterator_tag);
|
|
|
|
iterator InsertWithCount(const_iterator position, size_type n,
|
|
const_reference v);
|
|
|
|
template <typename ForwardIterator>
|
|
iterator InsertWithRange(const_iterator position, ForwardIterator first,
|
|
ForwardIterator last, std::forward_iterator_tag);
|
|
|
|
template <typename InputIterator>
|
|
iterator InsertWithRange(const_iterator position, InputIterator first,
|
|
InputIterator last, std::input_iterator_tag);
|
|
|
|
// Stores either the inlined or allocated representation
|
|
union Rep {
|
|
using ValueTypeBuffer =
|
|
absl::aligned_storage_t<sizeof(value_type), alignof(value_type)>;
|
|
using AllocationBuffer =
|
|
absl::aligned_storage_t<sizeof(Allocation), alignof(Allocation)>;
|
|
|
|
// Structs wrap the buffers to perform indirection that solves a bizarre
|
|
// compilation error on Visual Studio (all known versions).
|
|
struct InlinedRep {
|
|
ValueTypeBuffer inlined[N];
|
|
};
|
|
struct AllocatedRep {
|
|
AllocationBuffer allocation;
|
|
};
|
|
|
|
InlinedRep inlined_storage;
|
|
AllocatedRep allocation_storage;
|
|
};
|
|
|
|
AllocatorAndTag allocator_and_tag_;
|
|
Rep rep_;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// InlinedVector Non-Member Functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// `swap()`
|
|
//
|
|
// Swaps the contents of two inlined vectors. This convenience function
|
|
// simply calls `InlinedVector::swap()`.
|
|
template <typename T, size_t N, typename A>
|
|
void swap(InlinedVector<T, N, A>& a,
|
|
InlinedVector<T, N, A>& b) noexcept(noexcept(a.swap(b))) {
|
|
a.swap(b);
|
|
}
|
|
|
|
// `operator==()`
|
|
//
|
|
// Tests the equivalency of the contents of two inlined vectors.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator==(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return absl::equal(a.begin(), a.end(), b.begin(), b.end());
|
|
}
|
|
|
|
// `operator!=()`
|
|
//
|
|
// Tests the inequality of the contents of two inlined vectors.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator!=(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
// `operator<()`
|
|
//
|
|
// Tests whether the contents of one inlined vector are less than the contents
|
|
// of another through a lexicographical comparison operation.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator<(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
|
|
}
|
|
|
|
// `operator>()`
|
|
//
|
|
// Tests whether the contents of one inlined vector are greater than the
|
|
// contents of another through a lexicographical comparison operation.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator>(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return b < a;
|
|
}
|
|
|
|
// `operator<=()`
|
|
//
|
|
// Tests whether the contents of one inlined vector are less than or equal to
|
|
// the contents of another through a lexicographical comparison operation.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator<=(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return !(b < a);
|
|
}
|
|
|
|
// `operator>=()`
|
|
//
|
|
// Tests whether the contents of one inlined vector are greater than or equal to
|
|
// the contents of another through a lexicographical comparison operation.
|
|
template <typename T, size_t N, typename A>
|
|
bool operator>=(const InlinedVector<T, N, A>& a,
|
|
const InlinedVector<T, N, A>& b) {
|
|
return !(a < b);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Implementation of InlinedVector
|
|
//
|
|
// Do not depend on any below implementation details!
|
|
// -----------------------------------------------------------------------------
|
|
|
|
template <typename T, size_t N, typename A>
|
|
InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other)
|
|
: allocator_and_tag_(other.allocator()) {
|
|
reserve(other.size());
|
|
if (allocated()) {
|
|
UninitializedCopy(other.begin(), other.end(), allocated_space());
|
|
tag().set_allocated_size(other.size());
|
|
} else {
|
|
UninitializedCopy(other.begin(), other.end(), inlined_space());
|
|
tag().set_inline_size(other.size());
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
InlinedVector<T, N, A>::InlinedVector(const InlinedVector& other,
|
|
const allocator_type& alloc)
|
|
: allocator_and_tag_(alloc) {
|
|
reserve(other.size());
|
|
if (allocated()) {
|
|
UninitializedCopy(other.begin(), other.end(), allocated_space());
|
|
tag().set_allocated_size(other.size());
|
|
} else {
|
|
UninitializedCopy(other.begin(), other.end(), inlined_space());
|
|
tag().set_inline_size(other.size());
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other) noexcept(
|
|
absl::allocator_is_nothrow<allocator_type>::value ||
|
|
std::is_nothrow_move_constructible<value_type>::value)
|
|
: allocator_and_tag_(other.allocator_and_tag_) {
|
|
if (other.allocated()) {
|
|
// We can just steal the underlying buffer from the source.
|
|
// That leaves the source empty, so we clear its size.
|
|
init_allocation(other.allocation());
|
|
other.tag() = Tag();
|
|
} else {
|
|
UninitializedCopy(
|
|
std::make_move_iterator(other.inlined_space()),
|
|
std::make_move_iterator(other.inlined_space() + other.size()),
|
|
inlined_space());
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
InlinedVector<T, N, A>::InlinedVector(InlinedVector&& other,
|
|
const allocator_type& alloc) noexcept( //
|
|
absl::allocator_is_nothrow<allocator_type>::value)
|
|
: allocator_and_tag_(alloc) {
|
|
if (other.allocated()) {
|
|
if (alloc == other.allocator()) {
|
|
// We can just steal the allocation from the source.
|
|
tag() = other.tag();
|
|
init_allocation(other.allocation());
|
|
other.tag() = Tag();
|
|
} else {
|
|
// We need to use our own allocator
|
|
reserve(other.size());
|
|
UninitializedCopy(std::make_move_iterator(other.begin()),
|
|
std::make_move_iterator(other.end()),
|
|
allocated_space());
|
|
tag().set_allocated_size(other.size());
|
|
}
|
|
} else {
|
|
UninitializedCopy(
|
|
std::make_move_iterator(other.inlined_space()),
|
|
std::make_move_iterator(other.inlined_space() + other.size()),
|
|
inlined_space());
|
|
tag().set_inline_size(other.size());
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::InitAssign(size_type n, const_reference v) {
|
|
if (n > inlined_capacity()) {
|
|
Allocation new_allocation(allocator(), n);
|
|
init_allocation(new_allocation);
|
|
UninitializedFill(allocated_space(), allocated_space() + n, v);
|
|
tag().set_allocated_size(n);
|
|
} else {
|
|
UninitializedFill(inlined_space(), inlined_space() + n, v);
|
|
tag().set_inline_size(n);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::InitAssign(size_type n) {
|
|
if (n > inlined_capacity()) {
|
|
Allocation new_allocation(allocator(), n);
|
|
init_allocation(new_allocation);
|
|
UninitializedFill(allocated_space(), allocated_space() + n);
|
|
tag().set_allocated_size(n);
|
|
} else {
|
|
UninitializedFill(inlined_space(), inlined_space() + n);
|
|
tag().set_inline_size(n);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::resize(size_type n) {
|
|
size_type s = size();
|
|
if (n < s) {
|
|
erase(begin() + n, end());
|
|
return;
|
|
}
|
|
reserve(n);
|
|
assert(capacity() >= n);
|
|
|
|
// Fill new space with elements constructed in-place.
|
|
if (allocated()) {
|
|
UninitializedFill(allocated_space() + s, allocated_space() + n);
|
|
tag().set_allocated_size(n);
|
|
} else {
|
|
UninitializedFill(inlined_space() + s, inlined_space() + n);
|
|
tag().set_inline_size(n);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::resize(size_type n, const_reference v) {
|
|
size_type s = size();
|
|
if (n < s) {
|
|
erase(begin() + n, end());
|
|
return;
|
|
}
|
|
reserve(n);
|
|
assert(capacity() >= n);
|
|
|
|
// Fill new space with copies of 'v'.
|
|
if (allocated()) {
|
|
UninitializedFill(allocated_space() + s, allocated_space() + n, v);
|
|
tag().set_allocated_size(n);
|
|
} else {
|
|
UninitializedFill(inlined_space() + s, inlined_space() + n, v);
|
|
tag().set_inline_size(n);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename... Args>
|
|
auto InlinedVector<T, N, A>::emplace(const_iterator position, Args&&... args)
|
|
-> iterator {
|
|
assert(position >= begin());
|
|
assert(position <= end());
|
|
if (ABSL_PREDICT_FALSE(position == end())) {
|
|
emplace_back(std::forward<Args>(args)...);
|
|
return end() - 1;
|
|
}
|
|
|
|
T new_t = T(std::forward<Args>(args)...);
|
|
|
|
auto range = ShiftRight(position, 1);
|
|
if (range.first == range.second) {
|
|
// constructing into uninitialized memory
|
|
Construct(range.first, std::move(new_t));
|
|
} else {
|
|
// assigning into moved-from object
|
|
*range.first = T(std::move(new_t));
|
|
}
|
|
|
|
return range.first;
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
auto InlinedVector<T, N, A>::erase(const_iterator from, const_iterator to)
|
|
-> iterator {
|
|
assert(begin() <= from);
|
|
assert(from <= to);
|
|
assert(to <= end());
|
|
|
|
iterator range_start = const_cast<iterator>(from);
|
|
iterator range_end = const_cast<iterator>(to);
|
|
|
|
size_type s = size();
|
|
ptrdiff_t erase_gap = std::distance(range_start, range_end);
|
|
if (erase_gap > 0) {
|
|
pointer space;
|
|
if (allocated()) {
|
|
space = allocated_space();
|
|
tag().set_allocated_size(s - erase_gap);
|
|
} else {
|
|
space = inlined_space();
|
|
tag().set_inline_size(s - erase_gap);
|
|
}
|
|
std::move(range_end, space + s, range_start);
|
|
Destroy(space + s - erase_gap, space + s);
|
|
}
|
|
return range_start;
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::swap(InlinedVector& other) {
|
|
using std::swap; // Augment ADL with `std::swap`.
|
|
if (ABSL_PREDICT_FALSE(this == &other)) return;
|
|
|
|
if (allocated() && other.allocated()) {
|
|
// Both out of line, so just swap the tag, allocation, and allocator.
|
|
swap(tag(), other.tag());
|
|
swap(allocation(), other.allocation());
|
|
swap(allocator(), other.allocator());
|
|
return;
|
|
}
|
|
if (!allocated() && !other.allocated()) {
|
|
// Both inlined: swap up to smaller size, then move remaining elements.
|
|
InlinedVector* a = this;
|
|
InlinedVector* b = &other;
|
|
if (size() < other.size()) {
|
|
swap(a, b);
|
|
}
|
|
|
|
const size_type a_size = a->size();
|
|
const size_type b_size = b->size();
|
|
assert(a_size >= b_size);
|
|
// `a` is larger. Swap the elements up to the smaller array size.
|
|
std::swap_ranges(a->inlined_space(), a->inlined_space() + b_size,
|
|
b->inlined_space());
|
|
|
|
// Move the remaining elements:
|
|
// [`b_size`, `a_size`) from `a` -> [`b_size`, `a_size`) from `b`
|
|
b->UninitializedCopy(a->inlined_space() + b_size,
|
|
a->inlined_space() + a_size,
|
|
b->inlined_space() + b_size);
|
|
a->Destroy(a->inlined_space() + b_size, a->inlined_space() + a_size);
|
|
|
|
swap(a->tag(), b->tag());
|
|
swap(a->allocator(), b->allocator());
|
|
assert(b->size() == a_size);
|
|
assert(a->size() == b_size);
|
|
return;
|
|
}
|
|
|
|
// One is out of line, one is inline.
|
|
// We first move the elements from the inlined vector into the
|
|
// inlined space in the other vector. We then put the other vector's
|
|
// pointer/capacity into the originally inlined vector and swap
|
|
// the tags.
|
|
InlinedVector* a = this;
|
|
InlinedVector* b = &other;
|
|
if (a->allocated()) {
|
|
swap(a, b);
|
|
}
|
|
assert(!a->allocated());
|
|
assert(b->allocated());
|
|
const size_type a_size = a->size();
|
|
const size_type b_size = b->size();
|
|
// In an optimized build, `b_size` would be unused.
|
|
static_cast<void>(b_size);
|
|
|
|
// Made Local copies of `size()`, don't need `tag()` accurate anymore
|
|
swap(a->tag(), b->tag());
|
|
|
|
// Copy `b_allocation` out before `b`'s union gets clobbered by `inline_space`
|
|
Allocation b_allocation = b->allocation();
|
|
|
|
b->UninitializedCopy(a->inlined_space(), a->inlined_space() + a_size,
|
|
b->inlined_space());
|
|
a->Destroy(a->inlined_space(), a->inlined_space() + a_size);
|
|
|
|
a->allocation() = b_allocation;
|
|
|
|
if (a->allocator() != b->allocator()) {
|
|
swap(a->allocator(), b->allocator());
|
|
}
|
|
|
|
assert(b->size() == a_size);
|
|
assert(a->size() == b_size);
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::EnlargeBy(size_type delta) {
|
|
const size_type s = size();
|
|
assert(s <= capacity());
|
|
|
|
size_type target = (std::max)(inlined_capacity(), s + delta);
|
|
|
|
// Compute new capacity by repeatedly doubling current capacity
|
|
// TODO(psrc): Check and avoid overflow?
|
|
size_type new_capacity = capacity();
|
|
while (new_capacity < target) {
|
|
new_capacity <<= 1;
|
|
}
|
|
|
|
Allocation new_allocation(allocator(), new_capacity);
|
|
|
|
UninitializedCopy(std::make_move_iterator(data()),
|
|
std::make_move_iterator(data() + s),
|
|
new_allocation.buffer());
|
|
|
|
ResetAllocation(new_allocation, s);
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
auto InlinedVector<T, N, A>::ShiftRight(const_iterator position, size_type n)
|
|
-> std::pair<iterator, iterator> {
|
|
iterator start_used = const_cast<iterator>(position);
|
|
iterator start_raw = const_cast<iterator>(position);
|
|
size_type s = size();
|
|
size_type required_size = s + n;
|
|
|
|
if (required_size > capacity()) {
|
|
// Compute new capacity by repeatedly doubling current capacity
|
|
size_type new_capacity = capacity();
|
|
while (new_capacity < required_size) {
|
|
new_capacity <<= 1;
|
|
}
|
|
// Move everyone into the new allocation, leaving a gap of `n` for the
|
|
// requested shift.
|
|
Allocation new_allocation(allocator(), new_capacity);
|
|
size_type index = position - begin();
|
|
UninitializedCopy(std::make_move_iterator(data()),
|
|
std::make_move_iterator(data() + index),
|
|
new_allocation.buffer());
|
|
UninitializedCopy(std::make_move_iterator(data() + index),
|
|
std::make_move_iterator(data() + s),
|
|
new_allocation.buffer() + index + n);
|
|
ResetAllocation(new_allocation, s);
|
|
|
|
// New allocation means our iterator is invalid, so we'll recalculate.
|
|
// Since the entire gap is in new space, there's no used space to reuse.
|
|
start_raw = begin() + index;
|
|
start_used = start_raw;
|
|
} else {
|
|
// If we had enough space, it's a two-part move. Elements going into
|
|
// previously-unoccupied space need an `UninitializedCopy()`. Elements
|
|
// going into a previously-occupied space are just a `std::move()`.
|
|
iterator pos = const_cast<iterator>(position);
|
|
iterator raw_space = end();
|
|
size_type slots_in_used_space = raw_space - pos;
|
|
size_type new_elements_in_used_space = (std::min)(n, slots_in_used_space);
|
|
size_type new_elements_in_raw_space = n - new_elements_in_used_space;
|
|
size_type old_elements_in_used_space =
|
|
slots_in_used_space - new_elements_in_used_space;
|
|
|
|
UninitializedCopy(std::make_move_iterator(pos + old_elements_in_used_space),
|
|
std::make_move_iterator(raw_space),
|
|
raw_space + new_elements_in_raw_space);
|
|
std::move_backward(pos, pos + old_elements_in_used_space, raw_space);
|
|
|
|
// If the gap is entirely in raw space, the used space starts where the raw
|
|
// space starts, leaving no elements in used space. If the gap is entirely
|
|
// in used space, the raw space starts at the end of the gap, leaving all
|
|
// elements accounted for within the used space.
|
|
start_used = pos;
|
|
start_raw = pos + new_elements_in_used_space;
|
|
}
|
|
tag().add_size(n);
|
|
return std::make_pair(start_used, start_raw);
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
void InlinedVector<T, N, A>::Destroy(pointer from, pointer to) {
|
|
for (pointer cur = from; cur != to; ++cur) {
|
|
std::allocator_traits<allocator_type>::destroy(allocator(), cur);
|
|
}
|
|
#ifndef NDEBUG
|
|
// Overwrite unused memory with `0xab` so we can catch uninitialized usage.
|
|
// Cast to `void*` to tell the compiler that we don't care that we might be
|
|
// scribbling on a vtable pointer.
|
|
if (from != to) {
|
|
auto len = sizeof(value_type) * std::distance(from, to);
|
|
std::memset(reinterpret_cast<void*>(from), 0xab, len);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename Iterator>
|
|
void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last,
|
|
std::forward_iterator_tag) {
|
|
auto length = std::distance(first, last);
|
|
reserve(size() + length);
|
|
if (allocated()) {
|
|
UninitializedCopy(first, last, allocated_space() + size());
|
|
tag().set_allocated_size(size() + length);
|
|
} else {
|
|
UninitializedCopy(first, last, inlined_space() + size());
|
|
tag().set_inline_size(size() + length);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename Iterator>
|
|
void InlinedVector<T, N, A>::AppendRange(Iterator first, Iterator last,
|
|
std::input_iterator_tag) {
|
|
std::copy(first, last, std::back_inserter(*this));
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename Iterator>
|
|
void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
|
|
std::forward_iterator_tag) {
|
|
auto length = std::distance(first, last);
|
|
// Prefer reassignment to copy construction for elements.
|
|
if (static_cast<size_type>(length) <= size()) {
|
|
erase(std::copy(first, last, begin()), end());
|
|
return;
|
|
}
|
|
reserve(length);
|
|
iterator out = begin();
|
|
for (; out != end(); ++first, ++out) *out = *first;
|
|
if (allocated()) {
|
|
UninitializedCopy(first, last, out);
|
|
tag().set_allocated_size(length);
|
|
} else {
|
|
UninitializedCopy(first, last, out);
|
|
tag().set_inline_size(length);
|
|
}
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename Iterator>
|
|
void InlinedVector<T, N, A>::AssignRange(Iterator first, Iterator last,
|
|
std::input_iterator_tag) {
|
|
// Optimized to avoid reallocation.
|
|
// Prefer reassignment to copy construction for elements.
|
|
iterator out = begin();
|
|
for (; first != last && out != end(); ++first, ++out) {
|
|
*out = *first;
|
|
}
|
|
erase(out, end());
|
|
std::copy(first, last, std::back_inserter(*this));
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
auto InlinedVector<T, N, A>::InsertWithCount(const_iterator position,
|
|
size_type n, const_reference v)
|
|
-> iterator {
|
|
assert(position >= begin() && position <= end());
|
|
if (ABSL_PREDICT_FALSE(n == 0)) return const_cast<iterator>(position);
|
|
|
|
value_type copy = v;
|
|
std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
|
|
std::fill(it_pair.first, it_pair.second, copy);
|
|
UninitializedFill(it_pair.second, it_pair.first + n, copy);
|
|
|
|
return it_pair.first;
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename ForwardIterator>
|
|
auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
|
|
ForwardIterator first,
|
|
ForwardIterator last,
|
|
std::forward_iterator_tag)
|
|
-> iterator {
|
|
assert(position >= begin() && position <= end());
|
|
if (ABSL_PREDICT_FALSE(first == last)) return const_cast<iterator>(position);
|
|
|
|
auto n = std::distance(first, last);
|
|
std::pair<iterator, iterator> it_pair = ShiftRight(position, n);
|
|
size_type used_spots = it_pair.second - it_pair.first;
|
|
ForwardIterator open_spot = std::next(first, used_spots);
|
|
std::copy(first, open_spot, it_pair.first);
|
|
UninitializedCopy(open_spot, last, it_pair.second);
|
|
return it_pair.first;
|
|
}
|
|
|
|
template <typename T, size_t N, typename A>
|
|
template <typename InputIterator>
|
|
auto InlinedVector<T, N, A>::InsertWithRange(const_iterator position,
|
|
InputIterator first,
|
|
InputIterator last,
|
|
std::input_iterator_tag)
|
|
-> iterator {
|
|
assert(position >= begin() && position <= end());
|
|
size_type index = position - cbegin();
|
|
size_type i = index;
|
|
while (first != last) insert(begin() + i++, *first++);
|
|
return begin() + index;
|
|
}
|
|
|
|
} // namespace absl
|
|
|
|
#endif // ABSL_CONTAINER_INLINED_VECTOR_H_
|