Commit 4bb9e39c by Abseil Team Committed by dinord

Export of internal Abseil changes

--
f73e17cb24f7878933fc100bd9bfc39fce190b64 by Derek Mauro <dmauro@google.com>:

Internal change

PiperOrigin-RevId: 394306402

--
3d3eeffa4e37f63aa50fec1b90858043b40fe377 by Abseil Team <absl-team@google.com>:

Release a few more absl::Cord unit tests that were accidentally omitted from the OSS release before.

PiperOrigin-RevId: 394016464

--
8a77a8eb93d021aadd8fdf43e219bf35328001ad by CJ Johnson <johnsoncj@google.com>:

Fix typo in identifier

PiperOrigin-RevId: 394000560

--
d87206c7c8e045b03d74b91e47ef3db0eb47a17b by Derek Mauro <dmauro@google.com>:

Fix typo: RandenHwAes

PiperOrigin-RevId: 393879427

--
980a3402eea77b0c77fb20dd124203002ff791ba by Derek Mauro <dmauro@google.com>:

Adds macros `ABSL_LTS_RELEASE_VERSION` and `ABSL_LTS_RELEASE_PATCH_LEVEL`
to allow projects to detect if an LTS version is being used.

Fixes #1006

PiperOrigin-RevId: 393807178

--
aecc7ed34de718c64733dab76621eacb5af9af5f by CJ Johnson <johnsoncj@google.com>:

Change `alloc` to `allocator` to match the fact that other identifiers are full words

PiperOrigin-RevId: 393794869

--
ad754bbcf7b78f5d51ed5f39193ac3159429b2b4 by Derek Mauro <dmauro@google.com>:

Remove self-include of cord_rep_btree.h

PiperOrigin-RevId: 393792085

--
f8e937a0d8fe26400560754f3596e3c21bb6d0d7 by Abseil Team <absl-team@google.com>:

Fix trivial typo in comment.

PiperOrigin-RevId: 393770527

--
7a58ca5d708038d222c6a2b6ff5076b4ceffd370 by Tomas Dzetkulic <dzetkulic@google.com>:

Update Cord::AppendArray resize policy.

PiperOrigin-RevId: 393362184

--
316050d171190d9d6312cadf88b1cc2db2d1caa7 by Abseil Team <absl-team@google.com>:

Add a new top level profiling/ directory to the Abseil library

PiperOrigin-RevId: 393358109

--
0dbb8e10f7fa4a7ac74e12b178e936a67b266c51 by CJ Johnson <johnsoncj@google.com>:

Switch to the more common enable_if pattern of ` = 0` in InlinedVector

PiperOrigin-RevId: 393301549

--
136d3068ce33b50ac820e8bd01395a7164d5181f by Abseil Team <absl-team@google.com>:

Clean up typedefs in internal/inlined_vector.h

PiperOrigin-RevId: 393181754
GitOrigin-RevId: f73e17cb24f7878933fc100bd9bfc39fce190b64
Change-Id: I0c4cd4d71d97bd1bf651701b6302ea3d9ac59b66
parent 665ac5b4
...@@ -22,4 +22,4 @@ add_executable(simple simple.cc) ...@@ -22,4 +22,4 @@ add_executable(simple simple.cc)
find_package(absl REQUIRED) find_package(absl REQUIRED)
target_link_libraries(simple absl::strings) target_link_libraries(simple absl::strings absl::config)
...@@ -14,8 +14,17 @@ ...@@ -14,8 +14,17 @@
// limitations under the License. // limitations under the License.
#include <iostream> #include <iostream>
#include "absl/base/config.h"
#include "absl/strings/substitute.h" #include "absl/strings/substitute.h"
#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877
#error ABSL_LTS_RELEASE_VERSION is not set correctly.
#endif
#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0
#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly.
#endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]); std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]);
......
...@@ -92,6 +92,9 @@ Abseil contains the following C++ library components: ...@@ -92,6 +92,9 @@ Abseil contains the following C++ library components:
available within C++14 and C++17 versions of the C++ `<type_traits>` library. available within C++14 and C++17 versions of the C++ `<type_traits>` library.
* [`numeric`](absl/numeric/) * [`numeric`](absl/numeric/)
<br /> The `numeric` library contains C++11-compatible 128-bit integers. <br /> The `numeric` library contains C++11-compatible 128-bit integers.
* [`profiling`](absl/profiling/)
<br /> The `profiling` library contains utility code for profiling C++
entities. It is currently a private dependency of other Abseil libraries.
* [`status`](absl/status/) * [`status`](absl/status/)
<br /> The `status` contains abstractions for error handling, specifically <br /> The `status` contains abstractions for error handling, specifically
`absl::Status` and `absl::StatusOr<T>`. `absl::Status` and `absl::StatusOr<T>`.
......
...@@ -25,6 +25,7 @@ add_subdirectory(hash) ...@@ -25,6 +25,7 @@ add_subdirectory(hash)
add_subdirectory(memory) add_subdirectory(memory)
add_subdirectory(meta) add_subdirectory(meta)
add_subdirectory(numeric) add_subdirectory(numeric)
add_subdirectory(profiling)
add_subdirectory(random) add_subdirectory(random)
add_subdirectory(status) add_subdirectory(status)
add_subdirectory(strings) add_subdirectory(strings)
......
...@@ -66,6 +66,35 @@ ...@@ -66,6 +66,35 @@
#include "absl/base/options.h" #include "absl/base/options.h"
#include "absl/base/policy_checks.h" #include "absl/base/policy_checks.h"
// Abseil long-term support (LTS) releases will define
// `ABSL_LTS_RELEASE_VERSION` to the integer representing the date string of the
// LTS release version, and will define `ABSL_LTS_RELEASE_PATCH_LEVEL` to the
// integer representing the patch-level for that release.
//
// For example, for LTS release version "20300401.2", this would give us
// ABSL_LTS_RELEASE_VERSION == 20300401 && ABSL_LTS_RELEASE_PATCH_LEVEL == 2
//
// These symbols will not be defined in non-LTS code.
//
// Abseil recommends that clients live-at-head. Therefore, if you are using
// these symbols to assert a minimum version requirement, we recommend you do it
// as
//
// #if defined(ABSL_LTS_RELEASE_VERSION) && ABSL_LTS_RELEASE_VERSION < 20300401
// #error Project foo requires Abseil LTS version >= 20300401
// #endif
//
// The `defined(ABSL_LTS_RELEASE_VERSION)` part of the check excludes
// live-at-head clients from the minimum version assertion.
//
// See https://abseil.io/about/releases for more information on Abseil release
// management.
//
// LTS releases can be obtained from
// https://github.com/abseil/abseil-cpp/releases.
#undef ABSL_LTS_RELEASE_VERSION
#undef ABSL_LTS_RELEASE_PATCH_LEVEL
// Helper macro to convert a CPP variable to a string literal. // Helper macro to convert a CPP variable to a string literal.
#define ABSL_INTERNAL_DO_TOKEN_STR(x) #x #define ABSL_INTERNAL_DO_TOKEN_STR(x) #x
#define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x) #define ABSL_INTERNAL_TOKEN_STR(x) ABSL_INTERNAL_DO_TOKEN_STR(x)
......
...@@ -72,37 +72,43 @@ class InlinedVector { ...@@ -72,37 +72,43 @@ class InlinedVector {
using Storage = inlined_vector_internal::Storage<T, N, A>; using Storage = inlined_vector_internal::Storage<T, N, A>;
using AllocatorTraits = typename Storage::AllocatorTraits; template <typename TheA>
using RValueReference = typename Storage::RValueReference; using AllocatorTraits = inlined_vector_internal::AllocatorTraits<TheA>;
using MoveIterator = typename Storage::MoveIterator; template <typename TheA>
using IsMemcpyOk = typename Storage::IsMemcpyOk; using MoveIterator = inlined_vector_internal::MoveIterator<TheA>;
template <typename TheA>
template <typename Iterator> using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<TheA>;
template <typename TheA, typename Iterator>
using IteratorValueAdapter = using IteratorValueAdapter =
typename Storage::template IteratorValueAdapter<Iterator>; inlined_vector_internal::IteratorValueAdapter<TheA, Iterator>;
using CopyValueAdapter = typename Storage::CopyValueAdapter; template <typename TheA>
using DefaultValueAdapter = typename Storage::DefaultValueAdapter; using CopyValueAdapter = inlined_vector_internal::CopyValueAdapter<TheA>;
template <typename TheA>
using DefaultValueAdapter =
inlined_vector_internal::DefaultValueAdapter<TheA>;
template <typename Iterator> template <typename Iterator>
using EnableIfAtLeastForwardIterator = absl::enable_if_t< using EnableIfAtLeastForwardIterator = absl::enable_if_t<
inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
template <typename Iterator> template <typename Iterator>
using DisableIfAtLeastForwardIterator = absl::enable_if_t< using DisableIfAtLeastForwardIterator = absl::enable_if_t<
!inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value>; !inlined_vector_internal::IsAtLeastForwardIterator<Iterator>::value, int>;
public: public:
using allocator_type = typename Storage::allocator_type; using allocator_type = A;
using value_type = typename Storage::value_type; using value_type = inlined_vector_internal::ValueType<A>;
using pointer = typename Storage::pointer; using pointer = inlined_vector_internal::Pointer<A>;
using const_pointer = typename Storage::const_pointer; using const_pointer = inlined_vector_internal::ConstPointer<A>;
using size_type = typename Storage::size_type; using size_type = inlined_vector_internal::SizeType<A>;
using difference_type = typename Storage::difference_type; using difference_type = inlined_vector_internal::DifferenceType<A>;
using reference = typename Storage::reference; using reference = inlined_vector_internal::Reference<A>;
using const_reference = typename Storage::const_reference; using const_reference = inlined_vector_internal::ConstReference<A>;
using iterator = typename Storage::iterator; using iterator = inlined_vector_internal::Iterator<A>;
using const_iterator = typename Storage::const_iterator; using const_iterator = inlined_vector_internal::ConstIterator<A>;
using reverse_iterator = typename Storage::reverse_iterator; using reverse_iterator = inlined_vector_internal::ReverseIterator<A>;
using const_reverse_iterator = typename Storage::const_reverse_iterator; using const_reverse_iterator =
inlined_vector_internal::ConstReverseIterator<A>;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// InlinedVector Constructors and Destructor // InlinedVector Constructors and Destructor
...@@ -111,28 +117,28 @@ class InlinedVector { ...@@ -111,28 +117,28 @@ class InlinedVector {
// Creates an empty inlined vector with a value-initialized allocator. // Creates an empty inlined vector with a value-initialized allocator.
InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {} InlinedVector() noexcept(noexcept(allocator_type())) : storage_() {}
// Creates an empty inlined vector with a copy of `alloc`. // Creates an empty inlined vector with a copy of `allocator`.
explicit InlinedVector(const allocator_type& alloc) noexcept explicit InlinedVector(const allocator_type& allocator) noexcept
: storage_(alloc) {} : storage_(allocator) {}
// Creates an inlined vector with `n` copies of `value_type()`. // Creates an inlined vector with `n` copies of `value_type()`.
explicit InlinedVector(size_type n, explicit InlinedVector(size_type n,
const allocator_type& alloc = allocator_type()) const allocator_type& allocator = allocator_type())
: storage_(alloc) { : storage_(allocator) {
storage_.Initialize(DefaultValueAdapter(), n); storage_.Initialize(DefaultValueAdapter<A>(), n);
} }
// Creates an inlined vector with `n` copies of `v`. // Creates an inlined vector with `n` copies of `v`.
InlinedVector(size_type n, const_reference v, InlinedVector(size_type n, const_reference v,
const allocator_type& alloc = allocator_type()) const allocator_type& allocator = allocator_type())
: storage_(alloc) { : storage_(allocator) {
storage_.Initialize(CopyValueAdapter(v), n); storage_.Initialize(CopyValueAdapter<A>(std::addressof(v)), n);
} }
// Creates an inlined vector with copies of the elements of `list`. // Creates an inlined vector with copies of the elements of `list`.
InlinedVector(std::initializer_list<value_type> list, InlinedVector(std::initializer_list<value_type> list,
const allocator_type& alloc = allocator_type()) const allocator_type& allocator = allocator_type())
: InlinedVector(list.begin(), list.end(), alloc) {} : InlinedVector(list.begin(), list.end(), allocator) {}
// Creates an inlined vector with elements constructed from the provided // Creates an inlined vector with elements constructed from the provided
// forward iterator range [`first`, `last`). // forward iterator range [`first`, `last`).
...@@ -141,35 +147,36 @@ class InlinedVector { ...@@ -141,35 +147,36 @@ class InlinedVector {
// this constructor with two integral arguments and a call to the above // this constructor with two integral arguments and a call to the above
// `InlinedVector(size_type, const_reference)` constructor. // `InlinedVector(size_type, const_reference)` constructor.
template <typename ForwardIterator, template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
InlinedVector(ForwardIterator first, ForwardIterator last, InlinedVector(ForwardIterator first, ForwardIterator last,
const allocator_type& alloc = allocator_type()) const allocator_type& allocator = allocator_type())
: storage_(alloc) { : storage_(allocator) {
storage_.Initialize(IteratorValueAdapter<ForwardIterator>(first), storage_.Initialize(IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last)); std::distance(first, last));
} }
// Creates an inlined vector with elements constructed from the provided input // Creates an inlined vector with elements constructed from the provided input
// iterator range [`first`, `last`). // iterator range [`first`, `last`).
template <typename InputIterator, template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> DisableIfAtLeastForwardIterator<InputIterator> = 0>
InlinedVector(InputIterator first, InputIterator last, InlinedVector(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type()) const allocator_type& allocator = allocator_type())
: storage_(alloc) { : storage_(allocator) {
std::copy(first, last, std::back_inserter(*this)); std::copy(first, last, std::back_inserter(*this));
} }
// Creates an inlined vector by copying the contents of `other` using // Creates an inlined vector by copying the contents of `other` using
// `other`'s allocator. // `other`'s allocator.
InlinedVector(const InlinedVector& other) InlinedVector(const InlinedVector& other)
: InlinedVector(other, *other.storage_.GetAllocPtr()) {} : InlinedVector(other, other.storage_.GetAllocator()) {}
// Creates an inlined vector by copying the contents of `other` using `alloc`. // Creates an inlined vector by copying the contents of `other` using the
InlinedVector(const InlinedVector& other, const allocator_type& alloc) // provided `allocator`.
: storage_(alloc) { InlinedVector(const InlinedVector& other, const allocator_type& allocator)
: storage_(allocator) {
if (other.empty()) { if (other.empty()) {
// Empty; nothing to do. // Empty; nothing to do.
} else if (IsMemcpyOk::value && !other.storage_.GetIsAllocated()) { } else if (IsMemcpyOk<A>::value && !other.storage_.GetIsAllocated()) {
// Memcpy-able and do not need allocation. // Memcpy-able and do not need allocation.
storage_.MemcpyFrom(other.storage_); storage_.MemcpyFrom(other.storage_);
} else { } else {
...@@ -194,8 +201,8 @@ class InlinedVector { ...@@ -194,8 +201,8 @@ class InlinedVector {
InlinedVector(InlinedVector&& other) noexcept( InlinedVector(InlinedVector&& other) noexcept(
absl::allocator_is_nothrow<allocator_type>::value || absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value) std::is_nothrow_move_constructible<value_type>::value)
: storage_(*other.storage_.GetAllocPtr()) { : storage_(other.storage_.GetAllocator()) {
if (IsMemcpyOk::value) { if (IsMemcpyOk<A>::value) {
storage_.MemcpyFrom(other.storage_); storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0); other.storage_.SetInlinedSize(0);
...@@ -206,11 +213,11 @@ class InlinedVector { ...@@ -206,11 +213,11 @@ class InlinedVector {
other.storage_.SetInlinedSize(0); other.storage_.SetInlinedSize(0);
} else { } else {
IteratorValueAdapter<MoveIterator> other_values( IteratorValueAdapter<A, MoveIterator<A>> other_values(
MoveIterator(other.storage_.GetInlinedData())); MoveIterator<A>(other.storage_.GetInlinedData()));
inlined_vector_internal::ConstructElements( inlined_vector_internal::ConstructElements<A>(
storage_.GetAllocPtr(), storage_.GetInlinedData(), &other_values, storage_.GetAllocator(), storage_.GetInlinedData(), other_values,
other.storage_.GetSize()); other.storage_.GetSize());
storage_.SetInlinedSize(other.storage_.GetSize()); storage_.SetInlinedSize(other.storage_.GetSize());
...@@ -218,20 +225,22 @@ class InlinedVector { ...@@ -218,20 +225,22 @@ class InlinedVector {
} }
// Creates an inlined vector by moving in the contents of `other` with a copy // Creates an inlined vector by moving in the contents of `other` with a copy
// of `alloc`. // of `allocator`.
// //
// NOTE: if `other`'s allocator is not equal to `alloc`, even if `other` // NOTE: if `other`'s allocator is not equal to `allocator`, even if `other`
// contains allocated memory, this move constructor will still allocate. Since // contains allocated memory, this move constructor will still allocate. Since
// allocation is performed, this constructor can only be `noexcept` if the // allocation is performed, this constructor can only be `noexcept` if the
// specified allocator is also `noexcept`. // specified allocator is also `noexcept`.
InlinedVector(InlinedVector&& other, const allocator_type& alloc) noexcept( InlinedVector(
absl::allocator_is_nothrow<allocator_type>::value) InlinedVector&& other,
: storage_(alloc) { const allocator_type& allocator)
if (IsMemcpyOk::value) { noexcept(absl::allocator_is_nothrow<allocator_type>::value)
: storage_(allocator) {
if (IsMemcpyOk<A>::value) {
storage_.MemcpyFrom(other.storage_); storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0); other.storage_.SetInlinedSize(0);
} else if ((*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) && } else if ((storage_.GetAllocator() == other.storage_.GetAllocator()) &&
other.storage_.GetIsAllocated()) { other.storage_.GetIsAllocated()) {
storage_.SetAllocatedData(other.storage_.GetAllocatedData(), storage_.SetAllocatedData(other.storage_.GetAllocatedData(),
other.storage_.GetAllocatedCapacity()); other.storage_.GetAllocatedCapacity());
...@@ -239,8 +248,8 @@ class InlinedVector { ...@@ -239,8 +248,8 @@ class InlinedVector {
other.storage_.SetInlinedSize(0); other.storage_.SetInlinedSize(0);
} else { } else {
storage_.Initialize( storage_.Initialize(IteratorValueAdapter<A, MoveIterator<A>>(
IteratorValueAdapter<MoveIterator>(MoveIterator(other.data())), MoveIterator<A>(other.data())),
other.size()); other.size());
} }
} }
...@@ -442,7 +451,7 @@ class InlinedVector { ...@@ -442,7 +451,7 @@ class InlinedVector {
// `InlinedVector::get_allocator()` // `InlinedVector::get_allocator()`
// //
// Returns a copy of the inlined vector's allocator. // Returns a copy of the inlined vector's allocator.
allocator_type get_allocator() const { return *storage_.GetAllocPtr(); } allocator_type get_allocator() const { return storage_.GetAllocator(); }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// InlinedVector Member Mutators // InlinedVector Member Mutators
...@@ -476,16 +485,16 @@ class InlinedVector { ...@@ -476,16 +485,16 @@ class InlinedVector {
// unspecified state. // unspecified state.
InlinedVector& operator=(InlinedVector&& other) { InlinedVector& operator=(InlinedVector&& other) {
if (ABSL_PREDICT_TRUE(this != std::addressof(other))) { if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
if (IsMemcpyOk::value || other.storage_.GetIsAllocated()) { if (IsMemcpyOk<A>::value || other.storage_.GetIsAllocated()) {
inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(),
size()); data(), size());
storage_.DeallocateIfAllocated(); storage_.DeallocateIfAllocated();
storage_.MemcpyFrom(other.storage_); storage_.MemcpyFrom(other.storage_);
other.storage_.SetInlinedSize(0); other.storage_.SetInlinedSize(0);
} else { } else {
storage_.Assign(IteratorValueAdapter<MoveIterator>( storage_.Assign(IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator(other.storage_.GetInlinedData())), MoveIterator<A>(other.storage_.GetInlinedData())),
other.size()); other.size());
} }
} }
...@@ -497,7 +506,7 @@ class InlinedVector { ...@@ -497,7 +506,7 @@ class InlinedVector {
// //
// Replaces the contents of the inlined vector with `n` copies of `v`. // Replaces the contents of the inlined vector with `n` copies of `v`.
void assign(size_type n, const_reference v) { void assign(size_type n, const_reference v) {
storage_.Assign(CopyValueAdapter(v), n); storage_.Assign(CopyValueAdapter<A>(std::addressof(v)), n);
} }
// Overload of `InlinedVector::assign(...)` that replaces the contents of the // Overload of `InlinedVector::assign(...)` that replaces the contents of the
...@@ -511,9 +520,9 @@ class InlinedVector { ...@@ -511,9 +520,9 @@ class InlinedVector {
// //
// NOTE: this overload is for iterators that are "forward" category or better. // NOTE: this overload is for iterators that are "forward" category or better.
template <typename ForwardIterator, template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
void assign(ForwardIterator first, ForwardIterator last) { void assign(ForwardIterator first, ForwardIterator last) {
storage_.Assign(IteratorValueAdapter<ForwardIterator>(first), storage_.Assign(IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last)); std::distance(first, last));
} }
...@@ -522,7 +531,7 @@ class InlinedVector { ...@@ -522,7 +531,7 @@ class InlinedVector {
// //
// NOTE: this overload is for iterators that are "input" category. // NOTE: this overload is for iterators that are "input" category.
template <typename InputIterator, template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> DisableIfAtLeastForwardIterator<InputIterator> = 0>
void assign(InputIterator first, InputIterator last) { void assign(InputIterator first, InputIterator last) {
size_type i = 0; size_type i = 0;
for (; i < size() && first != last; ++i, static_cast<void>(++first)) { for (; i < size() && first != last; ++i, static_cast<void>(++first)) {
...@@ -541,7 +550,7 @@ class InlinedVector { ...@@ -541,7 +550,7 @@ class InlinedVector {
// is larger than `size()`, new elements are value-initialized. // is larger than `size()`, new elements are value-initialized.
void resize(size_type n) { void resize(size_type n) {
ABSL_HARDENING_ASSERT(n <= max_size()); ABSL_HARDENING_ASSERT(n <= max_size());
storage_.Resize(DefaultValueAdapter(), n); storage_.Resize(DefaultValueAdapter<A>(), n);
} }
// Overload of `InlinedVector::resize(...)` that resizes the inlined vector to // Overload of `InlinedVector::resize(...)` that resizes the inlined vector to
...@@ -551,7 +560,7 @@ class InlinedVector { ...@@ -551,7 +560,7 @@ class InlinedVector {
// is larger than `size()`, new elements are copied-constructed from `v`. // is larger than `size()`, new elements are copied-constructed from `v`.
void resize(size_type n, const_reference v) { void resize(size_type n, const_reference v) {
ABSL_HARDENING_ASSERT(n <= max_size()); ABSL_HARDENING_ASSERT(n <= max_size());
storage_.Resize(CopyValueAdapter(v), n); storage_.Resize(CopyValueAdapter<A>(std::addressof(v)), n);
} }
// `InlinedVector::insert(...)` // `InlinedVector::insert(...)`
...@@ -564,7 +573,7 @@ class InlinedVector { ...@@ -564,7 +573,7 @@ class InlinedVector {
// Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using
// move semantics, returning an `iterator` to the newly inserted element. // move semantics, returning an `iterator` to the newly inserted element.
iterator insert(const_iterator pos, RValueReference v) { iterator insert(const_iterator pos, value_type&& v) {
return emplace(pos, std::move(v)); return emplace(pos, std::move(v));
} }
...@@ -577,7 +586,8 @@ class InlinedVector { ...@@ -577,7 +586,8 @@ class InlinedVector {
if (ABSL_PREDICT_TRUE(n != 0)) { if (ABSL_PREDICT_TRUE(n != 0)) {
value_type dealias = v; value_type dealias = v;
return storage_.Insert(pos, CopyValueAdapter(dealias), n); return storage_.Insert(pos, CopyValueAdapter<A>(std::addressof(dealias)),
n);
} else { } else {
return const_cast<iterator>(pos); return const_cast<iterator>(pos);
} }
...@@ -596,14 +606,15 @@ class InlinedVector { ...@@ -596,14 +606,15 @@ class InlinedVector {
// //
// NOTE: this overload is for iterators that are "forward" category or better. // NOTE: this overload is for iterators that are "forward" category or better.
template <typename ForwardIterator, template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> EnableIfAtLeastForwardIterator<ForwardIterator> = 0>
iterator insert(const_iterator pos, ForwardIterator first, iterator insert(const_iterator pos, ForwardIterator first,
ForwardIterator last) { ForwardIterator last) {
ABSL_HARDENING_ASSERT(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
ABSL_HARDENING_ASSERT(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
if (ABSL_PREDICT_TRUE(first != last)) { if (ABSL_PREDICT_TRUE(first != last)) {
return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first), return storage_.Insert(pos,
IteratorValueAdapter<A, ForwardIterator>(first),
std::distance(first, last)); std::distance(first, last));
} else { } else {
return const_cast<iterator>(pos); return const_cast<iterator>(pos);
...@@ -616,7 +627,7 @@ class InlinedVector { ...@@ -616,7 +627,7 @@ class InlinedVector {
// //
// NOTE: this overload is for iterators that are "input" category. // NOTE: this overload is for iterators that are "input" category.
template <typename InputIterator, template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> DisableIfAtLeastForwardIterator<InputIterator> = 0>
iterator insert(const_iterator pos, InputIterator first, InputIterator last) { iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
ABSL_HARDENING_ASSERT(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
ABSL_HARDENING_ASSERT(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
...@@ -640,8 +651,8 @@ class InlinedVector { ...@@ -640,8 +651,8 @@ class InlinedVector {
value_type dealias(std::forward<Args>(args)...); value_type dealias(std::forward<Args>(args)...);
return storage_.Insert(pos, return storage_.Insert(pos,
IteratorValueAdapter<MoveIterator>( IteratorValueAdapter<A, MoveIterator<A>>(
MoveIterator(std::addressof(dealias))), MoveIterator<A>(std::addressof(dealias))),
1); 1);
} }
...@@ -661,7 +672,7 @@ class InlinedVector { ...@@ -661,7 +672,7 @@ class InlinedVector {
// Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()`
// using move semantics. // using move semantics.
void push_back(RValueReference v) { void push_back(value_type&& v) {
static_cast<void>(emplace_back(std::move(v))); static_cast<void>(emplace_back(std::move(v)));
} }
...@@ -671,7 +682,7 @@ class InlinedVector { ...@@ -671,7 +682,7 @@ class InlinedVector {
void pop_back() noexcept { void pop_back() noexcept {
ABSL_HARDENING_ASSERT(!empty()); ABSL_HARDENING_ASSERT(!empty());
AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); AllocatorTraits<A>::destroy(storage_.GetAllocator(), data() + (size() - 1));
storage_.SubtractSize(1); storage_.SubtractSize(1);
} }
...@@ -710,7 +721,7 @@ class InlinedVector { ...@@ -710,7 +721,7 @@ class InlinedVector {
// Destroys all elements in the inlined vector, setting the size to `0` and // Destroys all elements in the inlined vector, setting the size to `0` and
// deallocating any held memory. // deallocating any held memory.
void clear() noexcept { void clear() noexcept {
inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), data(), inlined_vector_internal::DestroyElements<A>(storage_.GetAllocator(), data(),
size()); size());
storage_.DeallocateIfAllocated(); storage_.DeallocateIfAllocated();
......
...@@ -40,45 +40,65 @@ namespace inlined_vector_internal { ...@@ -40,45 +40,65 @@ namespace inlined_vector_internal {
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif #endif
template <typename A>
using AllocatorTraits = std::allocator_traits<A>;
template <typename A>
using ValueType = typename AllocatorTraits<A>::value_type;
template <typename A>
using SizeType = typename AllocatorTraits<A>::size_type;
template <typename A>
using Pointer = typename AllocatorTraits<A>::pointer;
template <typename A>
using ConstPointer = typename AllocatorTraits<A>::const_pointer;
template <typename A>
using SizeType = typename AllocatorTraits<A>::size_type;
template <typename A>
using DifferenceType = typename AllocatorTraits<A>::difference_type;
template <typename A>
using Reference = ValueType<A>&;
template <typename A>
using ConstReference = const ValueType<A>&;
template <typename A>
using Iterator = Pointer<A>;
template <typename A>
using ConstIterator = ConstPointer<A>;
template <typename A>
using ReverseIterator = typename std::reverse_iterator<Iterator<A>>;
template <typename A>
using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>;
template <typename A>
using MoveIterator = typename std::move_iterator<Iterator<A>>;
template <typename Iterator> template <typename Iterator>
using IsAtLeastForwardIterator = std::is_convertible< using IsAtLeastForwardIterator = std::is_convertible<
typename std::iterator_traits<Iterator>::iterator_category, typename std::iterator_traits<Iterator>::iterator_category,
std::forward_iterator_tag>; std::forward_iterator_tag>;
template <typename AllocatorType, template <typename A>
typename ValueType =
typename absl::allocator_traits<AllocatorType>::value_type>
using IsMemcpyOk = using IsMemcpyOk =
absl::conjunction<std::is_same<AllocatorType, std::allocator<ValueType>>, absl::conjunction<std::is_same<A, std::allocator<ValueType<A>>>,
absl::is_trivially_copy_constructible<ValueType>, absl::is_trivially_copy_constructible<ValueType<A>>,
absl::is_trivially_copy_assignable<ValueType>, absl::is_trivially_copy_assignable<ValueType<A>>,
absl::is_trivially_destructible<ValueType>>; absl::is_trivially_destructible<ValueType<A>>>;
template <typename T>
struct TypeIdentity {
using type = T;
};
template <typename AllocatorType, typename Pointer, typename SizeType> // Used for function arguments in template functions to prevent ADL by forcing
void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, // callers to explicitly specify the template parameter.
SizeType destroy_size) { template <typename T>
using AllocatorTraits = absl::allocator_traits<AllocatorType>; using NoTypeDeduction = typename TypeIdentity<T>::type;
template <typename A>
void DestroyElements(NoTypeDeduction<A>& allocator, Pointer<A> destroy_first,
SizeType<A> destroy_size) {
if (destroy_first != nullptr) { if (destroy_first != nullptr) {
for (auto i = destroy_size; i != 0;) { for (auto i = destroy_size; i != 0;) {
--i; --i;
AllocatorTraits::destroy(*alloc_ptr, destroy_first + i); AllocatorTraits<A>::destroy(allocator, destroy_first + i);
} }
#if !defined(NDEBUG)
{
using ValueType = typename AllocatorTraits::value_type;
// 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.
void* memory_ptr = destroy_first;
auto memory_size = destroy_size * sizeof(ValueType);
std::memset(memory_ptr, 0xab, memory_size);
}
#endif // !defined(NDEBUG)
} }
} }
...@@ -99,54 +119,45 @@ inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) { ...@@ -99,54 +119,45 @@ inline void MemcpyIfAllowed<true>(void* dst, const void* src, size_t n) {
template <> template <>
inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {} inline void MemcpyIfAllowed<false>(void*, const void*, size_t) {}
template <typename AllocatorType, typename Pointer, typename ValueAdapter, template <typename A, typename ValueAdapter>
typename SizeType> void ConstructElements(NoTypeDeduction<A>& allocator,
void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, Pointer<A> construct_first, ValueAdapter& values,
ValueAdapter* values_ptr, SizeType construct_size) { SizeType<A> construct_size) {
for (SizeType i = 0; i < construct_size; ++i) { for (SizeType<A> i = 0; i < construct_size; ++i) {
ABSL_INTERNAL_TRY { ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); }
values_ptr->ConstructNext(alloc_ptr, construct_first + i);
}
ABSL_INTERNAL_CATCH_ANY { ABSL_INTERNAL_CATCH_ANY {
inlined_vector_internal::DestroyElements(alloc_ptr, construct_first, i); DestroyElements<A>(allocator, construct_first, i);
ABSL_INTERNAL_RETHROW; ABSL_INTERNAL_RETHROW;
} }
} }
} }
template <typename Pointer, typename ValueAdapter, typename SizeType> template <typename A, typename ValueAdapter>
void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, void AssignElements(Pointer<A> assign_first, ValueAdapter& values,
SizeType assign_size) { SizeType<A> assign_size) {
for (SizeType i = 0; i < assign_size; ++i) { for (SizeType<A> i = 0; i < assign_size; ++i) {
values_ptr->AssignNext(assign_first + i); values.AssignNext(assign_first + i);
} }
} }
template <typename AllocatorType> template <typename A>
struct StorageView { struct StorageView {
using AllocatorTraits = absl::allocator_traits<AllocatorType>; Pointer<A> data;
using Pointer = typename AllocatorTraits::pointer; SizeType<A> size;
using SizeType = typename AllocatorTraits::size_type; SizeType<A> capacity;
Pointer data;
SizeType size;
SizeType capacity;
}; };
template <typename AllocatorType, typename Iterator> template <typename A, typename Iterator>
class IteratorValueAdapter { class IteratorValueAdapter {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
using Pointer = typename AllocatorTraits::pointer;
public: public:
explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} explicit IteratorValueAdapter(const Iterator& it) : it_(it) {}
void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { void ConstructNext(A& allocator, Pointer<A> construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); AllocatorTraits<A>::construct(allocator, construct_at, *it_);
++it_; ++it_;
} }
void AssignNext(Pointer assign_at) { void AssignNext(Pointer<A> assign_at) {
*assign_at = *it_; *assign_at = *it_;
++it_; ++it_;
} }
...@@ -155,68 +166,55 @@ class IteratorValueAdapter { ...@@ -155,68 +166,55 @@ class IteratorValueAdapter {
Iterator it_; Iterator it_;
}; };
template <typename AllocatorType> template <typename A>
class CopyValueAdapter { class CopyValueAdapter {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
using ValueType = typename AllocatorTraits::value_type;
using Pointer = typename AllocatorTraits::pointer;
using ConstPointer = typename AllocatorTraits::const_pointer;
public: public:
explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} explicit CopyValueAdapter(ConstPointer<A> p) : ptr_(p) {}
void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { void ConstructNext(A& allocator, Pointer<A> construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); AllocatorTraits<A>::construct(allocator, construct_at, *ptr_);
} }
void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } void AssignNext(Pointer<A> assign_at) { *assign_at = *ptr_; }
private: private:
ConstPointer ptr_; ConstPointer<A> ptr_;
}; };
template <typename AllocatorType> template <typename A>
class DefaultValueAdapter { class DefaultValueAdapter {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
using ValueType = typename AllocatorTraits::value_type;
using Pointer = typename AllocatorTraits::pointer;
public: public:
explicit DefaultValueAdapter() {} explicit DefaultValueAdapter() {}
void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { void ConstructNext(A& allocator, Pointer<A> construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at); AllocatorTraits<A>::construct(allocator, construct_at);
} }
void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } void AssignNext(Pointer<A> assign_at) { *assign_at = ValueType<A>(); }
}; };
template <typename AllocatorType> template <typename A>
class AllocationTransaction { class AllocationTransaction {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
using Pointer = typename AllocatorTraits::pointer;
using SizeType = typename AllocatorTraits::size_type;
public: public:
explicit AllocationTransaction(AllocatorType* alloc_ptr) explicit AllocationTransaction(A& allocator)
: alloc_data_(*alloc_ptr, nullptr) {} : allocator_data_(allocator, nullptr), capacity_(0) {}
~AllocationTransaction() { ~AllocationTransaction() {
if (DidAllocate()) { if (DidAllocate()) {
AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity()); AllocatorTraits<A>::deallocate(GetAllocator(), GetData(), GetCapacity());
} }
} }
AllocationTransaction(const AllocationTransaction&) = delete; AllocationTransaction(const AllocationTransaction&) = delete;
void operator=(const AllocationTransaction&) = delete; void operator=(const AllocationTransaction&) = delete;
AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } A& GetAllocator() { return allocator_data_.template get<0>(); }
Pointer& GetData() { return alloc_data_.template get<1>(); } Pointer<A>& GetData() { return allocator_data_.template get<1>(); }
SizeType& GetCapacity() { return capacity_; } SizeType<A>& GetCapacity() { return capacity_; }
bool DidAllocate() { return GetData() != nullptr; } bool DidAllocate() { return GetData() != nullptr; }
Pointer Allocate(SizeType capacity) { Pointer<A> Allocate(SizeType<A> capacity) {
GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); GetData() = AllocatorTraits<A>::allocate(GetAllocator(), capacity);
GetCapacity() = capacity; GetCapacity() = capacity;
return GetData(); return GetData();
} }
...@@ -227,39 +225,33 @@ class AllocationTransaction { ...@@ -227,39 +225,33 @@ class AllocationTransaction {
} }
private: private:
container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; container_internal::CompressedTuple<A, Pointer<A>> allocator_data_;
SizeType capacity_ = 0; SizeType<A> capacity_;
}; };
template <typename AllocatorType> template <typename A>
class ConstructionTransaction { class ConstructionTransaction {
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
using Pointer = typename AllocatorTraits::pointer;
using SizeType = typename AllocatorTraits::size_type;
public: public:
explicit ConstructionTransaction(AllocatorType* alloc_ptr) explicit ConstructionTransaction(A& allocator)
: alloc_data_(*alloc_ptr, nullptr) {} : allocator_data_(allocator, nullptr), size_(0) {}
~ConstructionTransaction() { ~ConstructionTransaction() {
if (DidConstruct()) { if (DidConstruct()) {
inlined_vector_internal::DestroyElements(std::addressof(GetAllocator()), DestroyElements<A>(GetAllocator(), GetData(), GetSize());
GetData(), GetSize());
} }
} }
ConstructionTransaction(const ConstructionTransaction&) = delete; ConstructionTransaction(const ConstructionTransaction&) = delete;
void operator=(const ConstructionTransaction&) = delete; void operator=(const ConstructionTransaction&) = delete;
AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } A& GetAllocator() { return allocator_data_.template get<0>(); }
Pointer& GetData() { return alloc_data_.template get<1>(); } Pointer<A>& GetData() { return allocator_data_.template get<1>(); }
SizeType& GetSize() { return size_; } SizeType<A>& GetSize() { return size_; }
bool DidConstruct() { return GetData() != nullptr; } bool DidConstruct() { return GetData() != nullptr; }
template <typename ValueAdapter> template <typename ValueAdapter>
void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { void Construct(Pointer<A> data, ValueAdapter& values, SizeType<A> size) {
inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), ConstructElements<A>(GetAllocator(), data, values, size);
data, values_ptr, size);
GetData() = data; GetData() = data;
GetSize() = size; GetSize() = size;
} }
...@@ -269,52 +261,19 @@ class ConstructionTransaction { ...@@ -269,52 +261,19 @@ class ConstructionTransaction {
} }
private: private:
container_internal::CompressedTuple<AllocatorType, Pointer> alloc_data_; container_internal::CompressedTuple<A, Pointer<A>> allocator_data_;
SizeType size_ = 0; SizeType<A> size_;
}; };
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
class Storage { class Storage {
public: public:
using AllocatorTraits = absl::allocator_traits<A>; static SizeType<A> NextCapacity(SizeType<A> current_capacity) {
using allocator_type = typename AllocatorTraits::allocator_type;
using value_type = typename AllocatorTraits::value_type;
using pointer = typename AllocatorTraits::pointer;
using const_pointer = typename AllocatorTraits::const_pointer;
using size_type = typename AllocatorTraits::size_type;
using difference_type = typename AllocatorTraits::difference_type;
using reference = value_type&;
using const_reference = const value_type&;
using RValueReference = value_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>;
using MoveIterator = std::move_iterator<iterator>;
using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk<allocator_type>;
using StorageView = inlined_vector_internal::StorageView<allocator_type>;
template <typename Iterator>
using IteratorValueAdapter =
inlined_vector_internal::IteratorValueAdapter<allocator_type, Iterator>;
using CopyValueAdapter =
inlined_vector_internal::CopyValueAdapter<allocator_type>;
using DefaultValueAdapter =
inlined_vector_internal::DefaultValueAdapter<allocator_type>;
using AllocationTransaction =
inlined_vector_internal::AllocationTransaction<allocator_type>;
using ConstructionTransaction =
inlined_vector_internal::ConstructionTransaction<allocator_type>;
static size_type NextCapacity(size_type current_capacity) {
return current_capacity * 2; return current_capacity * 2;
} }
static size_type ComputeCapacity(size_type current_capacity, static SizeType<A> ComputeCapacity(SizeType<A> current_capacity,
size_type requested_capacity) { SizeType<A> requested_capacity) {
return (std::max)(NextCapacity(current_capacity), requested_capacity); return (std::max)(NextCapacity(current_capacity), requested_capacity);
} }
...@@ -322,15 +281,15 @@ class Storage { ...@@ -322,15 +281,15 @@ class Storage {
// Storage Constructors and Destructor // Storage Constructors and Destructor
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
Storage() : metadata_(allocator_type(), /* size and is_allocated */ 0) {} Storage() : metadata_(A(), /* size and is_allocated */ 0) {}
explicit Storage(const allocator_type& alloc) explicit Storage(const A& allocator)
: metadata_(alloc, /* size and is_allocated */ 0) {} : metadata_(allocator, /* size and is_allocated */ 0) {}
~Storage() { ~Storage() {
if (GetSizeAndIsAllocated() == 0) { if (GetSizeAndIsAllocated() == 0) {
// Empty and not allocated; nothing to do. // Empty and not allocated; nothing to do.
} else if (IsMemcpyOk::value) { } else if (IsMemcpyOk<A>::value) {
// No destructors need to be run; just deallocate if necessary. // No destructors need to be run; just deallocate if necessary.
DeallocateIfAllocated(); DeallocateIfAllocated();
} else { } else {
...@@ -342,52 +301,48 @@ class Storage { ...@@ -342,52 +301,48 @@ class Storage {
// Storage Member Accessors // Storage Member Accessors
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } SizeType<A>& GetSizeAndIsAllocated() { return metadata_.template get<1>(); }
const size_type& GetSizeAndIsAllocated() const { const SizeType<A>& GetSizeAndIsAllocated() const {
return metadata_.template get<1>(); return metadata_.template get<1>();
} }
size_type GetSize() const { return GetSizeAndIsAllocated() >> 1; } SizeType<A> GetSize() const { return GetSizeAndIsAllocated() >> 1; }
bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; }
pointer GetAllocatedData() { return data_.allocated.allocated_data; } Pointer<A> GetAllocatedData() { return data_.allocated.allocated_data; }
const_pointer GetAllocatedData() const { ConstPointer<A> GetAllocatedData() const {
return data_.allocated.allocated_data; return data_.allocated.allocated_data;
} }
pointer GetInlinedData() { Pointer<A> GetInlinedData() {
return reinterpret_cast<pointer>( return reinterpret_cast<Pointer<A>>(
std::addressof(data_.inlined.inlined_data[0])); std::addressof(data_.inlined.inlined_data[0]));
} }
const_pointer GetInlinedData() const { ConstPointer<A> GetInlinedData() const {
return reinterpret_cast<const_pointer>( return reinterpret_cast<ConstPointer<A>>(
std::addressof(data_.inlined.inlined_data[0])); std::addressof(data_.inlined.inlined_data[0]));
} }
size_type GetAllocatedCapacity() const { SizeType<A> GetAllocatedCapacity() const {
return data_.allocated.allocated_capacity; return data_.allocated.allocated_capacity;
} }
size_type GetInlinedCapacity() const { return static_cast<size_type>(N); } SizeType<A> GetInlinedCapacity() const { return static_cast<SizeType<A>>(N); }
StorageView MakeStorageView() { StorageView<A> MakeStorageView() {
return GetIsAllocated() return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(),
? StorageView{GetAllocatedData(), GetSize(),
GetAllocatedCapacity()} GetAllocatedCapacity()}
: StorageView{GetInlinedData(), GetSize(), GetInlinedCapacity()}; : StorageView<A>{GetInlinedData(), GetSize(),
GetInlinedCapacity()};
} }
allocator_type* GetAllocPtr() { A& GetAllocator() { return metadata_.template get<0>(); }
return std::addressof(metadata_.template get<0>());
}
const allocator_type* GetAllocPtr() const { const A& GetAllocator() const { return metadata_.template get<0>(); }
return std::addressof(metadata_.template get<0>());
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Storage Member Mutators // Storage Member Mutators
...@@ -396,74 +351,73 @@ class Storage { ...@@ -396,74 +351,73 @@ class Storage {
ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other);
template <typename ValueAdapter> template <typename ValueAdapter>
void Initialize(ValueAdapter values, size_type new_size); void Initialize(ValueAdapter values, SizeType<A> new_size);
template <typename ValueAdapter> template <typename ValueAdapter>
void Assign(ValueAdapter values, size_type new_size); void Assign(ValueAdapter values, SizeType<A> new_size);
template <typename ValueAdapter> template <typename ValueAdapter>
void Resize(ValueAdapter values, size_type new_size); void Resize(ValueAdapter values, SizeType<A> new_size);
template <typename ValueAdapter> template <typename ValueAdapter>
iterator Insert(const_iterator pos, ValueAdapter values, Iterator<A> Insert(ConstIterator<A> pos, ValueAdapter values,
size_type insert_count); SizeType<A> insert_count);
template <typename... Args> template <typename... Args>
reference EmplaceBack(Args&&... args); Reference<A> EmplaceBack(Args&&... args);
iterator Erase(const_iterator from, const_iterator to); Iterator<A> Erase(ConstIterator<A> from, ConstIterator<A> to);
void Reserve(size_type requested_capacity); void Reserve(SizeType<A> requested_capacity);
void ShrinkToFit(); void ShrinkToFit();
void Swap(Storage* other_storage_ptr); void Swap(Storage* other_storage_ptr);
void SetIsAllocated() { void SetIsAllocated() {
GetSizeAndIsAllocated() |= static_cast<size_type>(1); GetSizeAndIsAllocated() |= static_cast<SizeType<A>>(1);
} }
void UnsetIsAllocated() { void UnsetIsAllocated() {
GetSizeAndIsAllocated() &= ((std::numeric_limits<size_type>::max)() - 1); GetSizeAndIsAllocated() &= ((std::numeric_limits<SizeType<A>>::max)() - 1);
} }
void SetSize(size_type size) { void SetSize(SizeType<A> size) {
GetSizeAndIsAllocated() = GetSizeAndIsAllocated() =
(size << 1) | static_cast<size_type>(GetIsAllocated()); (size << 1) | static_cast<SizeType<A>>(GetIsAllocated());
} }
void SetAllocatedSize(size_type size) { void SetAllocatedSize(SizeType<A> size) {
GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1); GetSizeAndIsAllocated() = (size << 1) | static_cast<SizeType<A>>(1);
} }
void SetInlinedSize(size_type size) { void SetInlinedSize(SizeType<A> size) {
GetSizeAndIsAllocated() = size << static_cast<size_type>(1); GetSizeAndIsAllocated() = size << static_cast<SizeType<A>>(1);
} }
void AddSize(size_type count) { void AddSize(SizeType<A> count) {
GetSizeAndIsAllocated() += count << static_cast<size_type>(1); GetSizeAndIsAllocated() += count << static_cast<SizeType<A>>(1);
} }
void SubtractSize(size_type count) { void SubtractSize(SizeType<A> count) {
assert(count <= GetSize()); assert(count <= GetSize());
GetSizeAndIsAllocated() -= count << static_cast<size_type>(1); GetSizeAndIsAllocated() -= count << static_cast<SizeType<A>>(1);
} }
void SetAllocatedData(pointer data, size_type capacity) { void SetAllocatedData(Pointer<A> data, SizeType<A> capacity) {
data_.allocated.allocated_data = data; data_.allocated.allocated_data = data;
data_.allocated.allocated_capacity = capacity; data_.allocated.allocated_capacity = capacity;
} }
void AcquireAllocatedData(AllocationTransaction* allocation_tx_ptr) { void AcquireAllocatedData(AllocationTransaction<A>& allocation_tx) {
SetAllocatedData(allocation_tx_ptr->GetData(), SetAllocatedData(allocation_tx.GetData(), allocation_tx.GetCapacity());
allocation_tx_ptr->GetCapacity());
allocation_tx_ptr->Reset(); allocation_tx.Reset();
} }
void MemcpyFrom(const Storage& other_storage) { void MemcpyFrom(const Storage& other_storage) {
assert(IsMemcpyOk::value || other_storage.GetIsAllocated()); assert(IsMemcpyOk<A>::value || other_storage.GetIsAllocated());
GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();
data_ = other_storage.data_; data_ = other_storage.data_;
...@@ -471,7 +425,7 @@ class Storage { ...@@ -471,7 +425,7 @@ class Storage {
void DeallocateIfAllocated() { void DeallocateIfAllocated() {
if (GetIsAllocated()) { if (GetIsAllocated()) {
AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(), AllocatorTraits<A>::deallocate(GetAllocator(), GetAllocatedData(),
GetAllocatedCapacity()); GetAllocatedCapacity());
} }
} }
...@@ -479,16 +433,15 @@ class Storage { ...@@ -479,16 +433,15 @@ class Storage {
private: private:
ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); ABSL_ATTRIBUTE_NOINLINE void DestroyContents();
using Metadata = using Metadata = container_internal::CompressedTuple<A, SizeType<A>>;
container_internal::CompressedTuple<allocator_type, size_type>;
struct Allocated { struct Allocated {
pointer allocated_data; Pointer<A> allocated_data;
size_type allocated_capacity; SizeType<A> allocated_capacity;
}; };
struct Inlined { struct Inlined {
alignas(value_type) char inlined_data[sizeof(value_type[N])]; alignas(ValueType<A>) char inlined_data[sizeof(ValueType<A>[N])];
}; };
union Data { union Data {
...@@ -497,7 +450,7 @@ class Storage { ...@@ -497,7 +450,7 @@ class Storage {
}; };
template <typename... Args> template <typename... Args>
ABSL_ATTRIBUTE_NOINLINE reference EmplaceBackSlow(Args&&... args); ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args);
Metadata metadata_; Metadata metadata_;
Data data_; Data data_;
...@@ -505,8 +458,8 @@ class Storage { ...@@ -505,8 +458,8 @@ class Storage {
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
void Storage<T, N, A>::DestroyContents() { void Storage<T, N, A>::DestroyContents() {
pointer data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); Pointer<A> data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData();
inlined_vector_internal::DestroyElements(GetAllocPtr(), data, GetSize()); DestroyElements<A>(GetAllocator(), data, GetSize());
DeallocateIfAllocated(); DeallocateIfAllocated();
} }
...@@ -514,8 +467,8 @@ template <typename T, size_t N, typename A> ...@@ -514,8 +467,8 @@ template <typename T, size_t N, typename A>
void Storage<T, N, A>::InitFrom(const Storage& other) { void Storage<T, N, A>::InitFrom(const Storage& other) {
const auto n = other.GetSize(); const auto n = other.GetSize();
assert(n > 0); // Empty sources handled handled in caller. assert(n > 0); // Empty sources handled handled in caller.
const_pointer src; ConstPointer<A> src;
pointer dst; Pointer<A> dst;
if (!other.GetIsAllocated()) { if (!other.GetIsAllocated()) {
dst = GetInlinedData(); dst = GetInlinedData();
src = other.GetInlinedData(); src = other.GetInlinedData();
...@@ -523,43 +476,42 @@ void Storage<T, N, A>::InitFrom(const Storage& other) { ...@@ -523,43 +476,42 @@ void Storage<T, N, A>::InitFrom(const Storage& other) {
// Because this is only called from the `InlinedVector` constructors, it's // Because this is only called from the `InlinedVector` constructors, it's
// safe to take on the allocation with size `0`. If `ConstructElements(...)` // safe to take on the allocation with size `0`. If `ConstructElements(...)`
// throws, deallocation will be automatically handled by `~Storage()`. // throws, deallocation will be automatically handled by `~Storage()`.
size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), n); SizeType<A> new_capacity = ComputeCapacity(GetInlinedCapacity(), n);
dst = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); dst = AllocatorTraits<A>::allocate(GetAllocator(), new_capacity);
SetAllocatedData(dst, new_capacity); SetAllocatedData(dst, new_capacity);
src = other.GetAllocatedData(); src = other.GetAllocatedData();
} }
if (IsMemcpyOk::value) { if (IsMemcpyOk<A>::value) {
MemcpyIfAllowed<IsMemcpyOk::value>(dst, src, sizeof(dst[0]) * n); MemcpyIfAllowed<IsMemcpyOk<A>::value>(dst, src, sizeof(dst[0]) * n);
} else { } else {
auto values = IteratorValueAdapter<const_pointer>(src); auto values = IteratorValueAdapter<A, ConstPointer<A>>(src);
inlined_vector_internal::ConstructElements(GetAllocPtr(), dst, &values, n); ConstructElements<A>(GetAllocator(), dst, values, n);
} }
GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
} }
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename ValueAdapter> template <typename ValueAdapter>
auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) auto Storage<T, N, A>::Initialize(ValueAdapter values, SizeType<A> new_size)
-> void { -> void {
// Only callable from constructors! // Only callable from constructors!
assert(!GetIsAllocated()); assert(!GetIsAllocated());
assert(GetSize() == 0); assert(GetSize() == 0);
pointer construct_data; Pointer<A> construct_data;
if (new_size > GetInlinedCapacity()) { if (new_size > GetInlinedCapacity()) {
// Because this is only called from the `InlinedVector` constructors, it's // Because this is only called from the `InlinedVector` constructors, it's
// safe to take on the allocation with size `0`. If `ConstructElements(...)` // safe to take on the allocation with size `0`. If `ConstructElements(...)`
// throws, deallocation will be automatically handled by `~Storage()`. // throws, deallocation will be automatically handled by `~Storage()`.
size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); SizeType<A> new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size);
construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); construct_data = AllocatorTraits<A>::allocate(GetAllocator(), new_capacity);
SetAllocatedData(construct_data, new_capacity); SetAllocatedData(construct_data, new_capacity);
SetIsAllocated(); SetIsAllocated();
} else { } else {
construct_data = GetInlinedData(); construct_data = GetInlinedData();
} }
inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, ConstructElements<A>(GetAllocator(), construct_data, values, new_size);
&values, new_size);
// Since the initial size was guaranteed to be `0` and the allocated bit is // Since the initial size was guaranteed to be `0` and the allocated bit is
// already correct for either case, *adding* `new_size` gives us the correct // already correct for either case, *adding* `new_size` gives us the correct
...@@ -569,17 +521,18 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size) ...@@ -569,17 +521,18 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename ValueAdapter> template <typename ValueAdapter>
auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { auto Storage<T, N, A>::Assign(ValueAdapter values, SizeType<A> new_size)
StorageView storage_view = MakeStorageView(); -> void {
StorageView<A> storage_view = MakeStorageView();
AllocationTransaction allocation_tx(GetAllocPtr()); AllocationTransaction<A> allocation_tx(GetAllocator());
absl::Span<value_type> assign_loop; absl::Span<ValueType<A>> assign_loop;
absl::Span<value_type> construct_loop; absl::Span<ValueType<A>> construct_loop;
absl::Span<value_type> destroy_loop; absl::Span<ValueType<A>> destroy_loop;
if (new_size > storage_view.capacity) { if (new_size > storage_view.capacity) {
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); SizeType<A> new_capacity = ComputeCapacity(storage_view.capacity, new_size);
construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; construct_loop = {allocation_tx.Allocate(new_capacity), new_size};
destroy_loop = {storage_view.data, storage_view.size}; destroy_loop = {storage_view.data, storage_view.size};
} else if (new_size > storage_view.size) { } else if (new_size > storage_view.size) {
...@@ -591,18 +544,16 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { ...@@ -591,18 +544,16 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
} }
inlined_vector_internal::AssignElements(assign_loop.data(), &values, AssignElements<A>(assign_loop.data(), values, assign_loop.size());
assign_loop.size());
inlined_vector_internal::ConstructElements( ConstructElements<A>(GetAllocator(), construct_loop.data(), values,
GetAllocPtr(), construct_loop.data(), &values, construct_loop.size()); construct_loop.size());
inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(), DestroyElements<A>(GetAllocator(), destroy_loop.data(), destroy_loop.size());
destroy_loop.size());
if (allocation_tx.DidAllocate()) { if (allocation_tx.DidAllocate()) {
DeallocateIfAllocated(); DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
SetIsAllocated(); SetIsAllocated();
} }
...@@ -611,19 +562,18 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void { ...@@ -611,19 +562,18 @@ auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename ValueAdapter> template <typename ValueAdapter>
auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { auto Storage<T, N, A>::Resize(ValueAdapter values, SizeType<A> new_size)
StorageView storage_view = MakeStorageView(); -> void {
StorageView<A> storage_view = MakeStorageView();
auto* const base = storage_view.data; auto* const base = storage_view.data;
const size_type size = storage_view.size; const SizeType<A> size = storage_view.size;
auto* alloc = GetAllocPtr(); auto& alloc = GetAllocator();
if (new_size <= size) { if (new_size <= size) {
// Destroy extra old elements. // Destroy extra old elements.
inlined_vector_internal::DestroyElements(alloc, base + new_size, DestroyElements<A>(alloc, base + new_size, size - new_size);
size - new_size);
} else if (new_size <= storage_view.capacity) { } else if (new_size <= storage_view.capacity) {
// Construct new elements in place. // Construct new elements in place.
inlined_vector_internal::ConstructElements(alloc, base + size, &values, ConstructElements<A>(alloc, base + size, values, new_size - size);
new_size - size);
} else { } else {
// Steps: // Steps:
// a. Allocate new backing store. // a. Allocate new backing store.
...@@ -632,21 +582,21 @@ auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { ...@@ -632,21 +582,21 @@ auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
// d. Destroy all elements in old backing store. // d. Destroy all elements in old backing store.
// Use transactional wrappers for the first two steps so we can roll // Use transactional wrappers for the first two steps so we can roll
// back if necessary due to exceptions. // back if necessary due to exceptions.
AllocationTransaction allocation_tx(alloc); AllocationTransaction<A> allocation_tx(alloc);
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); SizeType<A> new_capacity = ComputeCapacity(storage_view.capacity, new_size);
pointer new_data = allocation_tx.Allocate(new_capacity); Pointer<A> new_data = allocation_tx.Allocate(new_capacity);
ConstructionTransaction construction_tx(alloc); ConstructionTransaction<A> construction_tx(alloc);
construction_tx.Construct(new_data + size, &values, new_size - size); construction_tx.Construct(new_data + size, values, new_size - size);
IteratorValueAdapter<MoveIterator> move_values((MoveIterator(base))); IteratorValueAdapter<A, MoveIterator<A>> move_values(
inlined_vector_internal::ConstructElements(alloc, new_data, &move_values, (MoveIterator<A>(base)));
size); ConstructElements<A>(alloc, new_data, move_values, size);
inlined_vector_internal::DestroyElements(alloc, base, size); DestroyElements<A>(alloc, base, size);
construction_tx.Commit(); construction_tx.Commit();
DeallocateIfAllocated(); DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
SetIsAllocated(); SetIsAllocated();
} }
SetSize(new_size); SetSize(new_size);
...@@ -654,74 +604,73 @@ auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void { ...@@ -654,74 +604,73 @@ auto Storage<T, N, A>::Resize(ValueAdapter values, size_type new_size) -> void {
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename ValueAdapter> template <typename ValueAdapter>
auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values, auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values,
size_type insert_count) -> iterator { SizeType<A> insert_count) -> Iterator<A> {
StorageView storage_view = MakeStorageView(); StorageView<A> storage_view = MakeStorageView();
size_type insert_index = SizeType<A> insert_index =
std::distance(const_iterator(storage_view.data), pos); std::distance(ConstIterator<A>(storage_view.data), pos);
size_type insert_end_index = insert_index + insert_count; SizeType<A> insert_end_index = insert_index + insert_count;
size_type new_size = storage_view.size + insert_count; SizeType<A> new_size = storage_view.size + insert_count;
if (new_size > storage_view.capacity) { if (new_size > storage_view.capacity) {
AllocationTransaction allocation_tx(GetAllocPtr()); AllocationTransaction<A> allocation_tx(GetAllocator());
ConstructionTransaction construction_tx(GetAllocPtr()); ConstructionTransaction<A> construction_tx(GetAllocator());
ConstructionTransaction move_construciton_tx(GetAllocPtr()); ConstructionTransaction<A> move_construction_tx(GetAllocator());
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(storage_view.data)); MoveIterator<A>(storage_view.data));
size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); SizeType<A> new_capacity = ComputeCapacity(storage_view.capacity, new_size);
pointer new_data = allocation_tx.Allocate(new_capacity); Pointer<A> new_data = allocation_tx.Allocate(new_capacity);
construction_tx.Construct(new_data + insert_index, &values, insert_count); construction_tx.Construct(new_data + insert_index, values, insert_count);
move_construciton_tx.Construct(new_data, &move_values, insert_index); move_construction_tx.Construct(new_data, move_values, insert_index);
inlined_vector_internal::ConstructElements( ConstructElements<A>(GetAllocator(), new_data + insert_end_index,
GetAllocPtr(), new_data + insert_end_index, &move_values, move_values, storage_view.size - insert_index);
storage_view.size - insert_index);
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
storage_view.size);
construction_tx.Commit(); construction_tx.Commit();
move_construciton_tx.Commit(); move_construction_tx.Commit();
DeallocateIfAllocated(); DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
SetAllocatedSize(new_size); SetAllocatedSize(new_size);
return iterator(new_data + insert_index); return Iterator<A>(new_data + insert_index);
} else { } else {
size_type move_construction_destination_index = SizeType<A> move_construction_destination_index =
(std::max)(insert_end_index, storage_view.size); (std::max)(insert_end_index, storage_view.size);
ConstructionTransaction move_construction_tx(GetAllocPtr()); ConstructionTransaction<A> move_construction_tx(GetAllocator());
IteratorValueAdapter<MoveIterator> move_construction_values( IteratorValueAdapter<A, MoveIterator<A>> move_construction_values(
MoveIterator(storage_view.data + MoveIterator<A>(storage_view.data +
(move_construction_destination_index - insert_count))); (move_construction_destination_index - insert_count)));
absl::Span<value_type> move_construction = { absl::Span<ValueType<A>> move_construction = {
storage_view.data + move_construction_destination_index, storage_view.data + move_construction_destination_index,
new_size - move_construction_destination_index}; new_size - move_construction_destination_index};
pointer move_assignment_values = storage_view.data + insert_index; Pointer<A> move_assignment_values = storage_view.data + insert_index;
absl::Span<value_type> move_assignment = { absl::Span<ValueType<A>> move_assignment = {
storage_view.data + insert_end_index, storage_view.data + insert_end_index,
move_construction_destination_index - insert_end_index}; move_construction_destination_index - insert_end_index};
absl::Span<value_type> insert_assignment = {move_assignment_values, absl::Span<ValueType<A>> insert_assignment = {move_assignment_values,
move_construction.size()}; move_construction.size()};
absl::Span<value_type> insert_construction = { absl::Span<ValueType<A>> insert_construction = {
insert_assignment.data() + insert_assignment.size(), insert_assignment.data() + insert_assignment.size(),
insert_count - insert_assignment.size()}; insert_count - insert_assignment.size()};
move_construction_tx.Construct(move_construction.data(), move_construction_tx.Construct(move_construction.data(),
&move_construction_values, move_construction_values,
move_construction.size()); move_construction.size());
for (pointer destination = move_assignment.data() + move_assignment.size(), for (Pointer<A>
destination = move_assignment.data() + move_assignment.size(),
last_destination = move_assignment.data(), last_destination = move_assignment.data(),
source = move_assignment_values + move_assignment.size(); source = move_assignment_values + move_assignment.size();
;) { ;) {
...@@ -731,29 +680,28 @@ auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values, ...@@ -731,29 +680,28 @@ auto Storage<T, N, A>::Insert(const_iterator pos, ValueAdapter values,
*destination = std::move(*source); *destination = std::move(*source);
} }
inlined_vector_internal::AssignElements(insert_assignment.data(), &values, AssignElements<A>(insert_assignment.data(), values,
insert_assignment.size()); insert_assignment.size());
inlined_vector_internal::ConstructElements( ConstructElements<A>(GetAllocator(), insert_construction.data(), values,
GetAllocPtr(), insert_construction.data(), &values,
insert_construction.size()); insert_construction.size());
move_construction_tx.Commit(); move_construction_tx.Commit();
AddSize(insert_count); AddSize(insert_count);
return iterator(storage_view.data + insert_index); return Iterator<A>(storage_view.data + insert_index);
} }
} }
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename... Args> template <typename... Args>
auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> Reference<A> {
StorageView storage_view = MakeStorageView(); StorageView<A> storage_view = MakeStorageView();
const auto n = storage_view.size; const auto n = storage_view.size;
if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) {
// Fast path; new element fits. // Fast path; new element fits.
pointer last_ptr = storage_view.data + n; Pointer<A> last_ptr = storage_view.data + n;
AllocatorTraits::construct(*GetAllocPtr(), last_ptr, AllocatorTraits<A>::construct(GetAllocator(), last_ptr,
std::forward<Args>(args)...); std::forward<Args>(args)...);
AddSize(1); AddSize(1);
return *last_ptr; return *last_ptr;
...@@ -764,87 +712,83 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference { ...@@ -764,87 +712,83 @@ auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> reference {
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
template <typename... Args> template <typename... Args>
auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> reference { auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> Reference<A> {
StorageView storage_view = MakeStorageView(); StorageView<A> storage_view = MakeStorageView();
AllocationTransaction allocation_tx(GetAllocPtr()); AllocationTransaction<A> allocation_tx(GetAllocator());
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(storage_view.data)); MoveIterator<A>(storage_view.data));
size_type new_capacity = NextCapacity(storage_view.capacity); SizeType<A> new_capacity = NextCapacity(storage_view.capacity);
pointer construct_data = allocation_tx.Allocate(new_capacity); Pointer<A> construct_data = allocation_tx.Allocate(new_capacity);
pointer last_ptr = construct_data + storage_view.size; Pointer<A> last_ptr = construct_data + storage_view.size;
// Construct new element. // Construct new element.
AllocatorTraits::construct(*GetAllocPtr(), last_ptr, AllocatorTraits<A>::construct(GetAllocator(), last_ptr,
std::forward<Args>(args)...); std::forward<Args>(args)...);
// Move elements from old backing store to new backing store. // Move elements from old backing store to new backing store.
ABSL_INTERNAL_TRY { ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements( ConstructElements<A>(GetAllocator(), allocation_tx.GetData(), move_values,
GetAllocPtr(), allocation_tx.GetData(), &move_values,
storage_view.size); storage_view.size);
} }
ABSL_INTERNAL_CATCH_ANY { ABSL_INTERNAL_CATCH_ANY {
AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); AllocatorTraits<A>::destroy(GetAllocator(), last_ptr);
ABSL_INTERNAL_RETHROW; ABSL_INTERNAL_RETHROW;
} }
// Destroy elements in old backing store. // Destroy elements in old backing store.
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
storage_view.size);
DeallocateIfAllocated(); DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
SetIsAllocated(); SetIsAllocated();
AddSize(1); AddSize(1);
return *last_ptr; return *last_ptr;
} }
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
auto Storage<T, N, A>::Erase(const_iterator from, const_iterator to) auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to)
-> iterator { -> Iterator<A> {
StorageView storage_view = MakeStorageView(); StorageView<A> storage_view = MakeStorageView();
size_type erase_size = std::distance(from, to); SizeType<A> erase_size = std::distance(from, to);
size_type erase_index = SizeType<A> erase_index =
std::distance(const_iterator(storage_view.data), from); std::distance(ConstIterator<A>(storage_view.data), from);
size_type erase_end_index = erase_index + erase_size; SizeType<A> erase_end_index = erase_index + erase_size;
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(storage_view.data + erase_end_index)); MoveIterator<A>(storage_view.data + erase_end_index));
inlined_vector_internal::AssignElements(storage_view.data + erase_index, AssignElements<A>(storage_view.data + erase_index, move_values,
&move_values,
storage_view.size - erase_end_index); storage_view.size - erase_end_index);
inlined_vector_internal::DestroyElements( DestroyElements<A>(GetAllocator(),
GetAllocPtr(), storage_view.data + (storage_view.size - erase_size), storage_view.data + (storage_view.size - erase_size),
erase_size); erase_size);
SubtractSize(erase_size); SubtractSize(erase_size);
return iterator(storage_view.data + erase_index); return Iterator<A>(storage_view.data + erase_index);
} }
template <typename T, size_t N, typename A> template <typename T, size_t N, typename A>
auto Storage<T, N, A>::Reserve(size_type requested_capacity) -> void { auto Storage<T, N, A>::Reserve(SizeType<A> requested_capacity) -> void {
StorageView storage_view = MakeStorageView(); StorageView<A> storage_view = MakeStorageView();
if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return;
AllocationTransaction allocation_tx(GetAllocPtr()); AllocationTransaction<A> allocation_tx(GetAllocator());
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(storage_view.data)); MoveIterator<A>(storage_view.data));
size_type new_capacity = SizeType<A> new_capacity =
ComputeCapacity(storage_view.capacity, requested_capacity); ComputeCapacity(storage_view.capacity, requested_capacity);
pointer new_data = allocation_tx.Allocate(new_capacity); Pointer<A> new_data = allocation_tx.Allocate(new_capacity);
inlined_vector_internal::ConstructElements(GetAllocPtr(), new_data, ConstructElements<A>(GetAllocator(), new_data, move_values,
&move_values, storage_view.size);
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
storage_view.size); storage_view.size);
DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
DeallocateIfAllocated(); DeallocateIfAllocated();
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
SetIsAllocated(); SetIsAllocated();
} }
...@@ -853,41 +797,40 @@ auto Storage<T, N, A>::ShrinkToFit() -> void { ...@@ -853,41 +797,40 @@ auto Storage<T, N, A>::ShrinkToFit() -> void {
// May only be called on allocated instances! // May only be called on allocated instances!
assert(GetIsAllocated()); assert(GetIsAllocated());
StorageView storage_view{GetAllocatedData(), GetSize(), StorageView<A> storage_view{GetAllocatedData(), GetSize(),
GetAllocatedCapacity()}; GetAllocatedCapacity()};
if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return;
AllocationTransaction allocation_tx(GetAllocPtr()); AllocationTransaction<A> allocation_tx(GetAllocator());
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(storage_view.data)); MoveIterator<A>(storage_view.data));
pointer construct_data; Pointer<A> construct_data;
if (storage_view.size > GetInlinedCapacity()) { if (storage_view.size > GetInlinedCapacity()) {
size_type new_capacity = storage_view.size; SizeType<A> new_capacity = storage_view.size;
construct_data = allocation_tx.Allocate(new_capacity); construct_data = allocation_tx.Allocate(new_capacity);
} else { } else {
construct_data = GetInlinedData(); construct_data = GetInlinedData();
} }
ABSL_INTERNAL_TRY { ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data, ConstructElements<A>(GetAllocator(), construct_data, move_values,
&move_values, storage_view.size); storage_view.size);
} }
ABSL_INTERNAL_CATCH_ANY { ABSL_INTERNAL_CATCH_ANY {
SetAllocatedData(storage_view.data, storage_view.capacity); SetAllocatedData(storage_view.data, storage_view.capacity);
ABSL_INTERNAL_RETHROW; ABSL_INTERNAL_RETHROW;
} }
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data, DestroyElements<A>(GetAllocator(), storage_view.data, storage_view.size);
storage_view.size);
AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data, AllocatorTraits<A>::deallocate(GetAllocator(), storage_view.data,
storage_view.capacity); storage_view.capacity);
if (allocation_tx.DidAllocate()) { if (allocation_tx.DidAllocate()) {
AcquireAllocatedData(&allocation_tx); AcquireAllocatedData(allocation_tx);
} else { } else {
UnsetIsAllocated(); UnsetIsAllocated();
} }
...@@ -905,20 +848,19 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { ...@@ -905,20 +848,19 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
Storage* large_ptr = other_storage_ptr; Storage* large_ptr = other_storage_ptr;
if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr);
for (size_type i = 0; i < small_ptr->GetSize(); ++i) { for (SizeType<A> i = 0; i < small_ptr->GetSize(); ++i) {
swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]);
} }
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); MoveIterator<A>(large_ptr->GetInlinedData() + small_ptr->GetSize()));
inlined_vector_internal::ConstructElements( ConstructElements<A>(large_ptr->GetAllocator(),
large_ptr->GetAllocPtr(), small_ptr->GetInlinedData() + small_ptr->GetSize(),
small_ptr->GetInlinedData() + small_ptr->GetSize(), &move_values, move_values,
large_ptr->GetSize() - small_ptr->GetSize()); large_ptr->GetSize() - small_ptr->GetSize());
inlined_vector_internal::DestroyElements( DestroyElements<A>(large_ptr->GetAllocator(),
large_ptr->GetAllocPtr(),
large_ptr->GetInlinedData() + small_ptr->GetSize(), large_ptr->GetInlinedData() + small_ptr->GetSize(),
large_ptr->GetSize() - small_ptr->GetSize()); large_ptr->GetSize() - small_ptr->GetSize());
} else { } else {
...@@ -926,17 +868,17 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { ...@@ -926,17 +868,17 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
Storage* inlined_ptr = other_storage_ptr; Storage* inlined_ptr = other_storage_ptr;
if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr);
StorageView allocated_storage_view{allocated_ptr->GetAllocatedData(), StorageView<A> allocated_storage_view{
allocated_ptr->GetSize(), allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(),
allocated_ptr->GetAllocatedCapacity()}; allocated_ptr->GetAllocatedCapacity()};
IteratorValueAdapter<MoveIterator> move_values( IteratorValueAdapter<A, MoveIterator<A>> move_values(
MoveIterator(inlined_ptr->GetInlinedData())); MoveIterator<A>(inlined_ptr->GetInlinedData()));
ABSL_INTERNAL_TRY { ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements( ConstructElements<A>(inlined_ptr->GetAllocator(),
inlined_ptr->GetAllocPtr(), allocated_ptr->GetInlinedData(), allocated_ptr->GetInlinedData(), move_values,
&move_values, inlined_ptr->GetSize()); inlined_ptr->GetSize());
} }
ABSL_INTERNAL_CATCH_ANY { ABSL_INTERNAL_CATCH_ANY {
allocated_ptr->SetAllocatedData(allocated_storage_view.data, allocated_ptr->SetAllocatedData(allocated_storage_view.data,
...@@ -944,16 +886,15 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { ...@@ -944,16 +886,15 @@ auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void {
ABSL_INTERNAL_RETHROW; ABSL_INTERNAL_RETHROW;
} }
inlined_vector_internal::DestroyElements(inlined_ptr->GetAllocPtr(), DestroyElements<A>(inlined_ptr->GetAllocator(),
inlined_ptr->GetInlinedData(), inlined_ptr->GetInlinedData(), inlined_ptr->GetSize());
inlined_ptr->GetSize());
inlined_ptr->SetAllocatedData(allocated_storage_view.data, inlined_ptr->SetAllocatedData(allocated_storage_view.data,
allocated_storage_view.capacity); allocated_storage_view.capacity);
} }
swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated());
swap(*GetAllocPtr(), *other_storage_ptr->GetAllocPtr()); swap(GetAllocator(), other_storage_ptr->GetAllocator());
} }
// End ignore "array-bounds" and "maybe-uninitialized" // End ignore "array-bounds" and "maybe-uninitialized"
......
# Copyright 2021 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
#
# https://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.
package(default_visibility = ["//visibility:private"])
licenses(["notice"])
# Copyright 2021 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
#
# https://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.
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
// a hardware accelerated implementation of randen, or whether it // a hardware accelerated implementation of randen, or whether it
// will contain stubs that exit the process. // will contain stubs that exit the process.
#if ABSL_HAVE_ACCELERATED_AES #if ABSL_HAVE_ACCELERATED_AES
// The following plaforms have implemented RandenHwAws. // The following plaforms have implemented RandenHwAes.
#if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \ #if defined(ABSL_ARCH_X86_64) || defined(ABSL_ARCH_X86_32) || \
defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \ defined(ABSL_ARCH_PPC) || defined(ABSL_ARCH_ARM) || \
defined(ABSL_ARCH_AARCH64) defined(ABSL_ARCH_AARCH64)
......
...@@ -689,13 +689,10 @@ void Cord::InlineRep::AppendArray(absl::string_view src, ...@@ -689,13 +689,10 @@ void Cord::InlineRep::AppendArray(absl::string_view src,
return; return;
} }
// Note: we don't concern ourselves if src aliases data stored in the // Allocate flat to be a perfect fit on first append exceeding inlined size.
// inlined data of 'this', as we update the InlineData only at the end. // Subsequent growth will use amortized growth until we reach maximum flat
// We are going from an inline size to beyond inline size. Make the new size // size.
// either double the inlined size, or the added size + 10%. rep = CordRepFlat::New(inline_length + src.size());
const size_t size1 = inline_length * 2 + src.size();
const size_t size2 = inline_length + src.size() / 10;
rep = CordRepFlat::New(std::max<size_t>(size1, size2));
appended = std::min(src.size(), rep->flat()->Capacity() - inline_length); appended = std::min(src.size(), rep->flat()->Capacity() - inline_length);
memcpy(rep->flat()->Data(), data_.as_chars(), inline_length); memcpy(rep->flat()->Data(), data_.as_chars(), inline_length);
memcpy(rep->flat()->Data() + inline_length, src.data(), appended); memcpy(rep->flat()->Data() + inline_length, src.data(), appended);
......
...@@ -1385,6 +1385,59 @@ TEST_P(CordTest, DiabolicalGrowth) { ...@@ -1385,6 +1385,59 @@ TEST_P(CordTest, DiabolicalGrowth) {
cord.EstimatedMemoryUsage()); cord.EstimatedMemoryUsage());
} }
// The following tests check support for >4GB cords in 64-bit binaries, and
// 2GB-4GB cords in 32-bit binaries. This function returns the large cord size
// that's appropriate for the binary.
// Construct a huge cord with the specified valid prefix.
static absl::Cord MakeHuge(absl::string_view prefix) {
absl::Cord cord;
if (sizeof(size_t) > 4) {
// In 64-bit binaries, test 64-bit Cord support.
const size_t size =
static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 314;
cord.Append(absl::MakeCordFromExternal(
absl::string_view(prefix.data(), size),
[](absl::string_view s) { DoNothing(s, nullptr); }));
} else {
// Cords are limited to 32-bit lengths in 32-bit binaries. The following
// tests check for use of "signed int" to represent Cord length/offset.
// However absl::string_view does not allow lengths >= (1u<<31), so we need
// to append in two parts;
const size_t s1 = (1u << 31) - 1;
// For shorter cord, `Append` copies the data rather than allocating a new
// node. The threshold is currently set to 511, so `s2` needs to be bigger
// to not trigger the copy.
const size_t s2 = 600;
cord.Append(absl::MakeCordFromExternal(
absl::string_view(prefix.data(), s1),
[](absl::string_view s) { DoNothing(s, nullptr); }));
cord.Append(absl::MakeCordFromExternal(
absl::string_view("", s2),
[](absl::string_view s) { DoNothing(s, nullptr); }));
}
return cord;
}
TEST_P(CordTest, HugeCord) {
absl::Cord cord = MakeHuge("huge cord");
EXPECT_LE(cord.size(), cord.EstimatedMemoryUsage());
EXPECT_GE(cord.size() + 100, cord.EstimatedMemoryUsage());
}
// Tests that Append() works ok when handed a self reference
TEST_P(CordTest, AppendSelf) {
// We run the test until data is ~16K
// This guarantees it covers small, medium and large data.
std::string control_data = "Abc";
absl::Cord data(control_data);
while (control_data.length() < 0x4000) {
data.Append(data);
control_data.append(control_data);
ASSERT_EQ(control_data, data);
}
}
TEST_P(CordTest, MakeFragmentedCordFromInitializerList) { TEST_P(CordTest, MakeFragmentedCordFromInitializerList) {
absl::Cord fragmented = absl::Cord fragmented =
absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"}); absl::MakeFragmentedCord({"A ", "fragmented ", "Cord"});
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_internal.h"
#include "absl/strings/internal/cord_rep_btree.h"
#include "absl/strings/internal/cord_rep_flat.h" #include "absl/strings/internal/cord_rep_flat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/types/span.h" #include "absl/types/span.h"
......
...@@ -100,7 +100,7 @@ class FormatSpecTemplate ...@@ -100,7 +100,7 @@ class FormatSpecTemplate
// We use the 'unavailable' attribute to give a better compiler error than // We use the 'unavailable' attribute to give a better compiler error than
// just 'method is deleted'. // just 'method is deleted'.
// To avoid checking the format twice, we just check that the format is // To avoid checking the format twice, we just check that the format is
// constexpr. If is it valid, then the overload below will kick in. // constexpr. If it is valid, then the overload below will kick in.
// We add the template here to make this overload have lower priority. // We add the template here to make this overload have lower priority.
template <typename = void> template <typename = void>
FormatSpecTemplate(const char* s) // NOLINT FormatSpecTemplate(const char* s) // NOLINT
......
...@@ -96,6 +96,13 @@ def main(argv): ...@@ -96,6 +96,13 @@ def main(argv):
# Replacement directives go here. # Replacement directives go here.
ReplaceStringsInFile( ReplaceStringsInFile(
'absl/base/config.h', {
'#undef ABSL_LTS_RELEASE_VERSION':
'#define ABSL_LTS_RELEASE_VERSION {}'.format(datestamp),
'#undef ABSL_LTS_RELEASE_PATCH_LEVEL':
'#define ABSL_LTS_RELEASE_PATCH_LEVEL 0'
})
ReplaceStringsInFile(
'absl/base/options.h', { 'absl/base/options.h', {
'#define ABSL_OPTION_USE_INLINE_NAMESPACE 0': '#define ABSL_OPTION_USE_INLINE_NAMESPACE 0':
'#define ABSL_OPTION_USE_INLINE_NAMESPACE 1', '#define ABSL_OPTION_USE_INLINE_NAMESPACE 1',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment