Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(924)

Unified Diff: third_party/WebKit/Source/wtf/Vector.h

Issue 2769603002: Move files in wtf/ to platform/wtf/ (Part 8). (Closed)
Patch Set: Rebase. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/wtf/PrintStream.h ('k') | third_party/WebKit/Source/wtf/VectorTraits.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/wtf/Vector.h
diff --git a/third_party/WebKit/Source/wtf/Vector.h b/third_party/WebKit/Source/wtf/Vector.h
index 1a7bba1318fbd6653d3ce85060c99955c4a0ad3d..eb2053649470e41ffc4f63d57c63d497052c85f3 100644
--- a/third_party/WebKit/Source/wtf/Vector.h
+++ b/third_party/WebKit/Source/wtf/Vector.h
@@ -1,1910 +1,9 @@
-/*
- * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
-#ifndef WTF_Vector_h
-#define WTF_Vector_h
+#include "platform/wtf/Vector.h"
-#include "wtf/Alignment.h"
-#include "wtf/ConditionalDestructor.h"
-#include "wtf/ContainerAnnotations.h"
-#include "wtf/Noncopyable.h"
-#include "wtf/NotFound.h"
-#include "wtf/StdLibExtras.h"
-#include "wtf/VectorTraits.h"
-#include "wtf/allocator/PartitionAllocator.h"
-#include <algorithm>
-#include <initializer_list>
-#include <iterator>
-#include <string.h>
-#include <utility>
-
-// For ASAN builds, disable inline buffers completely as they cause various
-// issues.
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
-#define INLINE_CAPACITY 0
-#else
-#define INLINE_CAPACITY inlineCapacity
-#endif
-
-namespace WTF {
-
-#if defined(MEMORY_SANITIZER_INITIAL_SIZE)
-static const size_t kInitialVectorSize = 1;
-#else
-#ifndef WTF_VECTOR_INITIAL_SIZE
-#define WTF_VECTOR_INITIAL_SIZE 4
-#endif
-static const size_t kInitialVectorSize = WTF_VECTOR_INITIAL_SIZE;
-#endif
-
-template <typename T, size_t inlineBuffer, typename Allocator>
-class Deque;
-
-//
-// Vector Traits
-//
-
-// Bunch of traits for Vector are defined here, with which you can customize
-// Vector's behavior. In most cases the default traits are appropriate, so you
-// usually don't have to specialize those traits by yourself.
-//
-// The behavior of the implementation below can be controlled by VectorTraits.
-// If you want to change the behavior of your type, take a look at VectorTraits
-// (defined in VectorTraits.h), too.
-
-template <bool needsDestruction, typename T>
-struct VectorDestructor;
-
-template <typename T>
-struct VectorDestructor<false, T> {
- STATIC_ONLY(VectorDestructor);
- static void destruct(T*, T*) {}
-};
-
-template <typename T>
-struct VectorDestructor<true, T> {
- STATIC_ONLY(VectorDestructor);
- static void destruct(T* begin, T* end) {
- for (T* cur = begin; cur != end; ++cur)
- cur->~T();
- }
-};
-
-template <bool unusedSlotsMustBeZeroed, typename T>
-struct VectorUnusedSlotClearer;
-
-template <typename T>
-struct VectorUnusedSlotClearer<false, T> {
- STATIC_ONLY(VectorUnusedSlotClearer);
- static void clear(T*, T*) {}
-#if DCHECK_IS_ON()
- static void checkCleared(const T*, const T*) {}
-#endif
-};
-
-template <typename T>
-struct VectorUnusedSlotClearer<true, T> {
- STATIC_ONLY(VectorUnusedSlotClearer);
- static void clear(T* begin, T* end) {
- memset(reinterpret_cast<void*>(begin), 0, sizeof(T) * (end - begin));
- }
-
-#if DCHECK_IS_ON()
- static void checkCleared(const T* begin, const T* end) {
- const unsigned char* unusedArea =
- reinterpret_cast<const unsigned char*>(begin);
- const unsigned char* endAddress =
- reinterpret_cast<const unsigned char*>(end);
- DCHECK_GE(endAddress, unusedArea);
- for (int i = 0; i < endAddress - unusedArea; ++i)
- DCHECK(!unusedArea[i]);
- }
-#endif
-};
-
-template <bool canInitializeWithMemset, typename T>
-struct VectorInitializer;
-
-template <typename T>
-struct VectorInitializer<false, T> {
- STATIC_ONLY(VectorInitializer);
- static void initialize(T* begin, T* end) {
- for (T* cur = begin; cur != end; ++cur)
- new (NotNull, cur) T;
- }
-};
-
-template <typename T>
-struct VectorInitializer<true, T> {
- STATIC_ONLY(VectorInitializer);
- static void initialize(T* begin, T* end) {
- memset(begin, 0,
- reinterpret_cast<char*>(end) - reinterpret_cast<char*>(begin));
- }
-};
-
-template <bool canMoveWithMemcpy, typename T>
-struct VectorMover;
-
-template <typename T>
-struct VectorMover<false, T> {
- STATIC_ONLY(VectorMover);
- static void move(T* src, T* srcEnd, T* dst) {
- while (src != srcEnd) {
- new (NotNull, dst) T(std::move(*src));
- src->~T();
- ++dst;
- ++src;
- }
- }
- static void moveOverlapping(T* src, T* srcEnd, T* dst) {
- if (src > dst) {
- move(src, srcEnd, dst);
- } else {
- T* dstEnd = dst + (srcEnd - src);
- while (src != srcEnd) {
- --srcEnd;
- --dstEnd;
- new (NotNull, dstEnd) T(std::move(*srcEnd));
- srcEnd->~T();
- }
- }
- }
- static void swap(T* src, T* srcEnd, T* dst) {
- std::swap_ranges(src, srcEnd, dst);
- }
-};
-
-template <typename T>
-struct VectorMover<true, T> {
- STATIC_ONLY(VectorMover);
- static void move(const T* src, const T* srcEnd, T* dst) {
- if (LIKELY(dst && src))
- memcpy(dst, src, reinterpret_cast<const char*>(srcEnd) -
- reinterpret_cast<const char*>(src));
- }
- static void moveOverlapping(const T* src, const T* srcEnd, T* dst) {
- if (LIKELY(dst && src))
- memmove(dst, src, reinterpret_cast<const char*>(srcEnd) -
- reinterpret_cast<const char*>(src));
- }
- static void swap(T* src, T* srcEnd, T* dst) {
- std::swap_ranges(reinterpret_cast<char*>(src),
- reinterpret_cast<char*>(srcEnd),
- reinterpret_cast<char*>(dst));
- }
-};
-
-template <bool canCopyWithMemcpy, typename T>
-struct VectorCopier;
-
-template <typename T>
-struct VectorCopier<false, T> {
- STATIC_ONLY(VectorCopier);
- template <typename U>
- static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) {
- while (src != srcEnd) {
- new (NotNull, dst) T(*src);
- ++dst;
- ++src;
- }
- }
-};
-
-template <typename T>
-struct VectorCopier<true, T> {
- STATIC_ONLY(VectorCopier);
- static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) {
- if (LIKELY(dst && src))
- memcpy(dst, src, reinterpret_cast<const char*>(srcEnd) -
- reinterpret_cast<const char*>(src));
- }
- template <typename U>
- static void uninitializedCopy(const U* src, const U* srcEnd, T* dst) {
- VectorCopier<false, T>::uninitializedCopy(src, srcEnd, dst);
- }
-};
-
-template <bool canFillWithMemset, typename T>
-struct VectorFiller;
-
-template <typename T>
-struct VectorFiller<false, T> {
- STATIC_ONLY(VectorFiller);
- static void uninitializedFill(T* dst, T* dstEnd, const T& val) {
- while (dst != dstEnd) {
- new (NotNull, dst) T(val);
- ++dst;
- }
- }
-};
-
-template <typename T>
-struct VectorFiller<true, T> {
- STATIC_ONLY(VectorFiller);
- static void uninitializedFill(T* dst, T* dstEnd, const T& val) {
- static_assert(sizeof(T) == sizeof(char), "size of type should be one");
-#if COMPILER(GCC) && defined(_FORTIFY_SOURCE)
- if (!__builtin_constant_p(dstEnd - dst) || (!(dstEnd - dst)))
- memset(dst, val, dstEnd - dst);
-#else
- memset(dst, val, dstEnd - dst);
-#endif
- }
-};
-
-template <bool canCompareWithMemcmp, typename T>
-struct VectorComparer;
-
-template <typename T>
-struct VectorComparer<false, T> {
- STATIC_ONLY(VectorComparer);
- static bool compare(const T* a, const T* b, size_t size) {
- DCHECK(a);
- DCHECK(b);
- return std::equal(a, a + size, b);
- }
-};
-
-template <typename T>
-struct VectorComparer<true, T> {
- STATIC_ONLY(VectorComparer);
- static bool compare(const T* a, const T* b, size_t size) {
- DCHECK(a);
- DCHECK(b);
- return memcmp(a, b, sizeof(T) * size) == 0;
- }
-};
-
-template <typename T>
-struct VectorElementComparer {
- STATIC_ONLY(VectorElementComparer);
- template <typename U>
- static bool compareElement(const T& left, const U& right) {
- return left == right;
- }
-};
-
-template <typename T>
-struct VectorElementComparer<std::unique_ptr<T>> {
- STATIC_ONLY(VectorElementComparer);
- template <typename U>
- static bool compareElement(const std::unique_ptr<T>& left, const U& right) {
- return left.get() == right;
- }
-};
-
-// A collection of all the traits used by Vector. This is basically an
-// implementation detail of Vector, and you probably don't want to change this.
-// If you want to customize Vector's behavior, you should specialize
-// VectorTraits instead (defined in VectorTraits.h).
-template <typename T>
-struct VectorTypeOperations {
- STATIC_ONLY(VectorTypeOperations);
- static void destruct(T* begin, T* end) {
- VectorDestructor<VectorTraits<T>::needsDestruction, T>::destruct(begin,
- end);
- }
-
- static void initialize(T* begin, T* end) {
- VectorInitializer<VectorTraits<T>::canInitializeWithMemset, T>::initialize(
- begin, end);
- }
-
- static void move(T* src, T* srcEnd, T* dst) {
- VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::move(src, srcEnd, dst);
- }
-
- static void moveOverlapping(T* src, T* srcEnd, T* dst) {
- VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::moveOverlapping(
- src, srcEnd, dst);
- }
-
- static void swap(T* src, T* srcEnd, T* dst) {
- VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::swap(src, srcEnd, dst);
- }
-
- static void uninitializedCopy(const T* src, const T* srcEnd, T* dst) {
- VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy(
- src, srcEnd, dst);
- }
-
- static void uninitializedFill(T* dst, T* dstEnd, const T& val) {
- VectorFiller<VectorTraits<T>::canFillWithMemset, T>::uninitializedFill(
- dst, dstEnd, val);
- }
-
- static bool compare(const T* a, const T* b, size_t size) {
- return VectorComparer<VectorTraits<T>::canCompareWithMemcmp, T>::compare(
- a, b, size);
- }
-
- template <typename U>
- static bool compareElement(const T& left, U&& right) {
- return VectorElementComparer<T>::compareElement(left,
- std::forward<U>(right));
- }
-};
-
-//
-// VectorBuffer
-//
-
-// VectorBuffer is an implementation detail of Vector and Deque. It manages
-// Vector's underlying buffer, and does operations like allocation or
-// expansion.
-//
-// Not meant for general consumption.
-
-template <typename T, bool hasInlineCapacity, typename Allocator>
-class VectorBufferBase {
- WTF_MAKE_NONCOPYABLE(VectorBufferBase);
- DISALLOW_NEW();
-
- public:
- void allocateBuffer(size_t newCapacity) {
- DCHECK(newCapacity);
- DCHECK_LE(newCapacity,
- Allocator::template maxElementCountInBackingStore<T>());
- size_t sizeToAllocate = allocationSize(newCapacity);
- if (hasInlineCapacity)
- m_buffer =
- Allocator::template allocateInlineVectorBacking<T>(sizeToAllocate);
- else
- m_buffer = Allocator::template allocateVectorBacking<T>(sizeToAllocate);
- m_capacity = sizeToAllocate / sizeof(T);
- }
-
- void allocateExpandedBuffer(size_t newCapacity) {
- DCHECK(newCapacity);
- size_t sizeToAllocate = allocationSize(newCapacity);
- if (hasInlineCapacity)
- m_buffer =
- Allocator::template allocateInlineVectorBacking<T>(sizeToAllocate);
- else
- m_buffer =
- Allocator::template allocateExpandedVectorBacking<T>(sizeToAllocate);
- m_capacity = sizeToAllocate / sizeof(T);
- }
-
- size_t allocationSize(size_t capacity) const {
- return Allocator::template quantizedSize<T>(capacity);
- }
-
- T* buffer() { return m_buffer; }
- const T* buffer() const { return m_buffer; }
- size_t capacity() const { return m_capacity; }
-
- void clearUnusedSlots(T* from, T* to) {
- // If the vector backing is garbage-collected and needs tracing or
- // finalizing, we clear out the unused slots so that the visitor or the
- // finalizer does not cause a problem when visiting the unused slots.
- VectorUnusedSlotClearer<
- Allocator::isGarbageCollected &&
- (VectorTraits<T>::needsDestruction ||
- IsTraceableInCollectionTrait<VectorTraits<T>>::value),
- T>::clear(from, to);
- }
-
- void checkUnusedSlots(const T* from, const T* to) {
-#if DCHECK_IS_ON() && !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
- VectorUnusedSlotClearer<
- Allocator::isGarbageCollected &&
- (VectorTraits<T>::needsDestruction ||
- IsTraceableInCollectionTrait<VectorTraits<T>>::value),
- T>::checkCleared(from, to);
-#endif
- }
-
- // |end| is exclusive, a la STL.
- struct OffsetRange final {
- OffsetRange() : begin(0), end(0) {}
- explicit OffsetRange(size_t begin, size_t end) : begin(begin), end(end) {
- DCHECK_LE(begin, end);
- }
- bool empty() const { return begin == end; }
- size_t begin;
- size_t end;
- };
-
- protected:
- VectorBufferBase() : m_buffer(nullptr), m_capacity(0) {}
-
- VectorBufferBase(T* buffer, size_t capacity)
- : m_buffer(buffer), m_capacity(capacity) {}
-
- T* m_buffer;
- unsigned m_capacity;
- unsigned m_size;
-};
-
-template <typename T,
- size_t inlineCapacity,
- typename Allocator = PartitionAllocator>
-class VectorBuffer;
-
-template <typename T, typename Allocator>
-class VectorBuffer<T, 0, Allocator>
- : protected VectorBufferBase<T, false, Allocator> {
- private:
- using Base = VectorBufferBase<T, false, Allocator>;
-
- public:
- using OffsetRange = typename Base::OffsetRange;
-
- VectorBuffer() {}
-
- explicit VectorBuffer(size_t capacity) {
- // Calling malloc(0) might take a lock and may actually do an allocation
- // on some systems.
- if (capacity)
- allocateBuffer(capacity);
- }
-
- void destruct() {
- deallocateBuffer(m_buffer);
- m_buffer = nullptr;
- }
-
- void deallocateBuffer(T* bufferToDeallocate) {
- Allocator::freeVectorBacking(bufferToDeallocate);
- }
-
- bool expandBuffer(size_t newCapacity) {
- size_t sizeToAllocate = allocationSize(newCapacity);
- if (Allocator::expandVectorBacking(m_buffer, sizeToAllocate)) {
- m_capacity = sizeToAllocate / sizeof(T);
- return true;
- }
- return false;
- }
-
- inline bool shrinkBuffer(size_t newCapacity) {
- DCHECK_LT(newCapacity, capacity());
- size_t sizeToAllocate = allocationSize(newCapacity);
- if (Allocator::shrinkVectorBacking(m_buffer, allocationSize(capacity()),
- sizeToAllocate)) {
- m_capacity = sizeToAllocate / sizeof(T);
- return true;
- }
- return false;
- }
-
- void resetBufferPointer() {
- m_buffer = nullptr;
- m_capacity = 0;
- }
-
- // See the other specialization for the meaning of |thisHole| and |otherHole|.
- // They are irrelevant in this case.
- void swapVectorBuffer(VectorBuffer<T, 0, Allocator>& other,
- OffsetRange thisHole,
- OffsetRange otherHole) {
- static_assert(VectorTraits<T>::canSwapUsingCopyOrMove,
- "Cannot swap HeapVectors of TraceWrapperMembers.");
-
- std::swap(m_buffer, other.m_buffer);
- std::swap(m_capacity, other.m_capacity);
- std::swap(m_size, other.m_size);
- }
-
- using Base::allocateBuffer;
- using Base::allocationSize;
-
- using Base::buffer;
- using Base::capacity;
-
- using Base::clearUnusedSlots;
- using Base::checkUnusedSlots;
-
- bool hasOutOfLineBuffer() const {
- // When inlineCapacity is 0 we have an out of line buffer if we have a
- // buffer.
- return buffer();
- }
-
- T** bufferSlot() { return &m_buffer; }
-
- protected:
- using Base::m_size;
-
- private:
- using Base::m_buffer;
- using Base::m_capacity;
-};
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-class VectorBuffer : protected VectorBufferBase<T, true, Allocator> {
- WTF_MAKE_NONCOPYABLE(VectorBuffer);
-
- private:
- using Base = VectorBufferBase<T, true, Allocator>;
-
- public:
- using OffsetRange = typename Base::OffsetRange;
-
- VectorBuffer() : Base(inlineBuffer(), inlineCapacity) {}
-
- explicit VectorBuffer(size_t capacity)
- : Base(inlineBuffer(), inlineCapacity) {
- if (capacity > inlineCapacity)
- Base::allocateBuffer(capacity);
- }
-
- void destruct() {
- deallocateBuffer(m_buffer);
- m_buffer = nullptr;
- }
-
- NEVER_INLINE void reallyDeallocateBuffer(T* bufferToDeallocate) {
- Allocator::freeInlineVectorBacking(bufferToDeallocate);
- }
-
- void deallocateBuffer(T* bufferToDeallocate) {
- if (UNLIKELY(bufferToDeallocate != inlineBuffer()))
- reallyDeallocateBuffer(bufferToDeallocate);
- }
-
- bool expandBuffer(size_t newCapacity) {
- DCHECK_GT(newCapacity, inlineCapacity);
- if (m_buffer == inlineBuffer())
- return false;
-
- size_t sizeToAllocate = allocationSize(newCapacity);
- if (Allocator::expandInlineVectorBacking(m_buffer, sizeToAllocate)) {
- m_capacity = sizeToAllocate / sizeof(T);
- return true;
- }
- return false;
- }
-
- inline bool shrinkBuffer(size_t newCapacity) {
- DCHECK_LT(newCapacity, capacity());
- if (newCapacity <= inlineCapacity) {
- // We need to switch to inlineBuffer. Vector::shrinkCapacity will
- // handle it.
- return false;
- }
- DCHECK_NE(m_buffer, inlineBuffer());
- size_t newSize = allocationSize(newCapacity);
- if (!Allocator::shrinkInlineVectorBacking(
- m_buffer, allocationSize(capacity()), newSize))
- return false;
- m_capacity = newSize / sizeof(T);
- return true;
- }
-
- void resetBufferPointer() {
- m_buffer = inlineBuffer();
- m_capacity = inlineCapacity;
- }
-
- void allocateBuffer(size_t newCapacity) {
- // FIXME: This should DCHECK(!m_buffer) to catch misuse/leaks.
- if (newCapacity > inlineCapacity)
- Base::allocateBuffer(newCapacity);
- else
- resetBufferPointer();
- }
-
- void allocateExpandedBuffer(size_t newCapacity) {
- if (newCapacity > inlineCapacity)
- Base::allocateExpandedBuffer(newCapacity);
- else
- resetBufferPointer();
- }
-
- size_t allocationSize(size_t capacity) const {
- if (capacity <= inlineCapacity)
- return m_inlineBufferSize;
- return Base::allocationSize(capacity);
- }
-
- // Swap two vector buffers, both of which have the same non-zero inline
- // capacity.
- //
- // If the data is in an out-of-line buffer, we can just pass the pointers
- // across the two buffers. If the data is in an inline buffer, we need to
- // either swap or move each element, depending on whether each slot is
- // occupied or not.
- //
- // Further complication comes from the fact that VectorBuffer is also used as
- // the backing store of a Deque. Deque allocates the objects like a ring
- // buffer, so there may be a "hole" (unallocated region) in the middle of the
- // buffer. This function assumes elements in a range [m_buffer, m_buffer +
- // m_size) are all allocated except for elements within |thisHole|. The same
- // applies for |other.m_buffer| and |otherHole|.
- void swapVectorBuffer(VectorBuffer<T, inlineCapacity, Allocator>& other,
- OffsetRange thisHole,
- OffsetRange otherHole) {
- using TypeOperations = VectorTypeOperations<T>;
-
- static_assert(VectorTraits<T>::canSwapUsingCopyOrMove,
- "Cannot swap HeapVectors of TraceWrapperMembers.");
-
- if (buffer() != inlineBuffer() && other.buffer() != other.inlineBuffer()) {
- // The easiest case: both buffers are non-inline. We just need to swap the
- // pointers.
- std::swap(m_buffer, other.m_buffer);
- std::swap(m_capacity, other.m_capacity);
- std::swap(m_size, other.m_size);
- return;
- }
-
- Allocator::enterGCForbiddenScope();
-
- // Otherwise, we at least need to move some elements from one inline buffer
- // to another.
- //
- // Terminology: "source" is a place from which elements are copied, and
- // "destination" is a place to which elements are copied. thisSource or
- // otherSource can be empty (represented by nullptr) when this range or
- // other range is in an out-of-line buffer.
- //
- // We first record which range needs to get moved and where elements in such
- // a range will go. Elements in an inline buffer will go to the other
- // buffer's inline buffer. Elements in an out-of-line buffer won't move,
- // because we can just swap pointers of out-of-line buffers.
- T* thisSourceBegin = nullptr;
- size_t thisSourceSize = 0;
- T* thisDestinationBegin = nullptr;
- if (buffer() == inlineBuffer()) {
- thisSourceBegin = buffer();
- thisSourceSize = m_size;
- thisDestinationBegin = other.inlineBuffer();
- if (!thisHole.empty()) { // Sanity check.
- DCHECK_LT(thisHole.begin, thisHole.end);
- DCHECK_LE(thisHole.end, thisSourceSize);
- }
- } else {
- // We don't need the hole information for an out-of-line buffer.
- thisHole.begin = thisHole.end = 0;
- }
- T* otherSourceBegin = nullptr;
- size_t otherSourceSize = 0;
- T* otherDestinationBegin = nullptr;
- if (other.buffer() == other.inlineBuffer()) {
- otherSourceBegin = other.buffer();
- otherSourceSize = other.m_size;
- otherDestinationBegin = inlineBuffer();
- if (!otherHole.empty()) {
- DCHECK_LT(otherHole.begin, otherHole.end);
- DCHECK_LE(otherHole.end, otherSourceSize);
- }
- } else {
- otherHole.begin = otherHole.end = 0;
- }
-
- // Next, we mutate members and do other bookkeeping. We do pointer swapping
- // (for out-of-line buffers) here if we can. From now on, don't assume
- // buffer() or capacity() maintains their original values.
- std::swap(m_capacity, other.m_capacity);
- if (thisSourceBegin &&
- !otherSourceBegin) { // Our buffer is inline, theirs is not.
- DCHECK_EQ(buffer(), inlineBuffer());
- DCHECK_NE(other.buffer(), other.inlineBuffer());
- ANNOTATE_DELETE_BUFFER(m_buffer, inlineCapacity, m_size);
- m_buffer = other.buffer();
- other.m_buffer = other.inlineBuffer();
- std::swap(m_size, other.m_size);
- ANNOTATE_NEW_BUFFER(other.m_buffer, inlineCapacity, other.m_size);
- } else if (!thisSourceBegin &&
- otherSourceBegin) { // Their buffer is inline, ours is not.
- DCHECK_NE(buffer(), inlineBuffer());
- DCHECK_EQ(other.buffer(), other.inlineBuffer());
- ANNOTATE_DELETE_BUFFER(other.m_buffer, inlineCapacity, other.m_size);
- other.m_buffer = buffer();
- m_buffer = inlineBuffer();
- std::swap(m_size, other.m_size);
- ANNOTATE_NEW_BUFFER(m_buffer, inlineCapacity, m_size);
- } else { // Both buffers are inline.
- DCHECK(thisSourceBegin);
- DCHECK(otherSourceBegin);
- DCHECK_EQ(buffer(), inlineBuffer());
- DCHECK_EQ(other.buffer(), other.inlineBuffer());
- ANNOTATE_CHANGE_SIZE(m_buffer, inlineCapacity, m_size, other.m_size);
- ANNOTATE_CHANGE_SIZE(other.m_buffer, inlineCapacity, other.m_size,
- m_size);
- std::swap(m_size, other.m_size);
- }
-
- // We are ready to move elements. We determine an action for each "section",
- // which is a contiguous range such that all elements in the range are
- // treated similarly.
- size_t sectionBegin = 0;
- while (sectionBegin < inlineCapacity) {
- // To determine the end of this section, we list up all the boundaries
- // where the "occupiedness" may change.
- size_t sectionEnd = inlineCapacity;
- if (thisSourceBegin && sectionBegin < thisSourceSize)
- sectionEnd = std::min(sectionEnd, thisSourceSize);
- if (!thisHole.empty() && sectionBegin < thisHole.begin)
- sectionEnd = std::min(sectionEnd, thisHole.begin);
- if (!thisHole.empty() && sectionBegin < thisHole.end)
- sectionEnd = std::min(sectionEnd, thisHole.end);
- if (otherSourceBegin && sectionBegin < otherSourceSize)
- sectionEnd = std::min(sectionEnd, otherSourceSize);
- if (!otherHole.empty() && sectionBegin < otherHole.begin)
- sectionEnd = std::min(sectionEnd, otherHole.begin);
- if (!otherHole.empty() && sectionBegin < otherHole.end)
- sectionEnd = std::min(sectionEnd, otherHole.end);
-
- DCHECK_LT(sectionBegin, sectionEnd);
-
- // Is the |sectionBegin|-th element of |thisSource| occupied?
- bool thisOccupied = false;
- if (thisSourceBegin && sectionBegin < thisSourceSize) {
- // Yes, it's occupied, unless the position is in a hole.
- if (thisHole.empty() || sectionBegin < thisHole.begin ||
- sectionBegin >= thisHole.end)
- thisOccupied = true;
- }
- bool otherOccupied = false;
- if (otherSourceBegin && sectionBegin < otherSourceSize) {
- if (otherHole.empty() || sectionBegin < otherHole.begin ||
- sectionBegin >= otherHole.end)
- otherOccupied = true;
- }
-
- if (thisOccupied && otherOccupied) {
- // Both occupied; swap them. In this case, one's destination must be the
- // other's source (i.e. both ranges are in inline buffers).
- DCHECK_EQ(thisDestinationBegin, otherSourceBegin);
- DCHECK_EQ(otherDestinationBegin, thisSourceBegin);
- TypeOperations::swap(thisSourceBegin + sectionBegin,
- thisSourceBegin + sectionEnd,
- otherSourceBegin + sectionBegin);
- } else if (thisOccupied) {
- // Move from ours to theirs.
- TypeOperations::move(thisSourceBegin + sectionBegin,
- thisSourceBegin + sectionEnd,
- thisDestinationBegin + sectionBegin);
- Base::clearUnusedSlots(thisSourceBegin + sectionBegin,
- thisSourceBegin + sectionEnd);
- } else if (otherOccupied) {
- // Move from theirs to ours.
- TypeOperations::move(otherSourceBegin + sectionBegin,
- otherSourceBegin + sectionEnd,
- otherDestinationBegin + sectionBegin);
- Base::clearUnusedSlots(otherSourceBegin + sectionBegin,
- otherSourceBegin + sectionEnd);
- } else {
- // Both empty; nothing to do.
- }
-
- sectionBegin = sectionEnd;
- }
-
- Allocator::leaveGCForbiddenScope();
- }
-
- using Base::buffer;
- using Base::capacity;
-
- bool hasOutOfLineBuffer() const {
- return buffer() && buffer() != inlineBuffer();
- }
-
- T** bufferSlot() { return &m_buffer; }
-
- protected:
- using Base::m_size;
-
- private:
- using Base::m_buffer;
- using Base::m_capacity;
-
- static const size_t m_inlineBufferSize = inlineCapacity * sizeof(T);
- T* inlineBuffer() { return reinterpret_cast_ptr<T*>(m_inlineBuffer.buffer); }
- const T* inlineBuffer() const {
- return reinterpret_cast_ptr<const T*>(m_inlineBuffer.buffer);
- }
-
- AlignedBuffer<m_inlineBufferSize, WTF_ALIGN_OF(T)> m_inlineBuffer;
- template <typename U, size_t inlineBuffer, typename V>
- friend class Deque;
-};
-
-//
-// Vector
-//
-
-// Vector is a container that works just like std::vector. WTF's Vector has
-// several extra functionalities: inline buffer, behavior customization via
-// traits, and Oilpan support. Those are explained in the sections below.
-//
-// Vector is the most basic container, which stores its element in a contiguous
-// buffer. The buffer is expanded automatically when necessary. The elements
-// are automatically moved to the new buffer. This event is called a
-// reallocation. A reallocation takes O(N)-time (N = number of elements), but
-// its occurrences are rare, so its time cost should not be significant,
-// compared to the time cost of other operations to the vector.
-//
-// Time complexity of key operations is as follows:
-//
-// * Indexed access -- O(1)
-// * Insertion or removal of an element at the end -- amortized O(1)
-// * Other insertion or removal -- O(N)
-// * Swapping with another vector -- O(1)
-//
-// 1. Iterator invalidation semantics
-//
-// Vector provides STL-compatible iterators and reverse iterators. Iterators
-// are _invalidated_ on certain occasions. Reading an invalidated iterator
-// causes undefined behavior.
-//
-// Iterators are invalidated on the following situations:
-//
-// * When a reallocation happens on a vector, all the iterators for that
-// vector will be invalidated.
-// * Some member functions invalidate part of the existing iterators for
-// the vector; see comments on the individual functions.
-// * [Oilpan only] Heap compaction invalidates all the iterators for any
-// HeapVectors. This means you can only store an iterator on stack, as
-// a local variable.
-//
-// In this context, pointers or references to an element of a Vector are
-// essentially equivalent to iterators, in that they also become invalid
-// whenever corresponding iterators are invalidated.
-//
-// 2. Inline buffer
-//
-// Vectors may have an _inline buffer_. An inline buffer is a storage area
-// that is contained in the vector itself, along with other metadata like
-// m_size. It is used as a storage space when the vector's elements fit in
-// that space. If the inline buffer becomes full and further space is
-// necessary, an out-of-line buffer is allocated in the heap, and it will
-// take over the role of the inline buffer.
-//
-// The existence of an inline buffer is indicated by non-zero |inlineCapacity|
-// template argument. The value represents the number of elements that can be
-// stored in the inline buffer. Zero |inlineCapacity| means the vector has no
-// inline buffer.
-//
-// An inline buffer increases the size of the Vector instances, and, in trade
-// for that, it gives you several performance benefits, as long as the number
-// of elements do not exceed |inlineCapacity|:
-//
-// * No heap allocation will be made.
-// * Memory locality will improve.
-//
-// Generally, having an inline buffer is useful for vectors that (1) are
-// frequently accessed or modified, and (2) contain only a few elements at
-// most.
-//
-// 3. Behavior customization
-//
-// You usually do not need to customize Vector's behavior, since the default
-// behavior is appropriate for normal usage. The behavior is controlled by
-// VectorTypeOperations traits template above. Read VectorTypeOperations
-// and VectorTraits if you want to change the behavior for your types (i.e.
-// if you really want faster vector operations).
-//
-// The default traits basically do the following:
-//
-// * Skip constructor call and fill zeros with memset for simple types;
-// * Skip destructor call for simple types;
-// * Copy or move by memcpy for simple types; and
-// * Customize the comparisons for smart pointer types, so you can look
-// up a std::unique_ptr<T> element with a raw pointer, for instance.
-//
-// 4. Oilpan
-//
-// If you want to store garbage collected objects in Vector, (1) use HeapVector
-// (defined in HeapAllocator.h) instead of Vector, and (2) make sure your
-// garbage-collected type is wrapped with Member, like:
-//
-// HeapVector<Member<Node>> nodes;
-//
-// Unlike normal garbage-collected objects, a HeapVector object itself is
-// NOT a garbage-collected object, but its backing buffer is allocated in
-// Oilpan heap, and it may still carry garbage-collected objects.
-//
-// Even though a HeapVector object is not garbage-collected, you still need
-// to trace it, if you stored it in your class. Also, you can allocate it
-// as a local variable. This is useful when you want to build a vector locally
-// and put it in an on-heap vector with swap().
-//
-// Also, heap compaction, which may happen at any time when Blink code is not
-// running (i.e. Blink code does not appear in the call stack), may invalidate
-// existing iterators for any HeapVectors. So, essentially, you should always
-// allocate an iterator on stack (as a local variable), and you should not
-// store iterators in another heap object.
-
-template <typename T,
- size_t inlineCapacity = 0,
- typename Allocator = PartitionAllocator>
-class Vector
- : private VectorBuffer<T, INLINE_CAPACITY, Allocator>,
- // Heap-allocated vectors with no inlineCapacity never need a destructor.
- public ConditionalDestructor<Vector<T, INLINE_CAPACITY, Allocator>,
- (INLINE_CAPACITY == 0) &&
- Allocator::isGarbageCollected> {
- USE_ALLOCATOR(Vector, Allocator);
- using Base = VectorBuffer<T, INLINE_CAPACITY, Allocator>;
- using TypeOperations = VectorTypeOperations<T>;
- using OffsetRange = typename Base::OffsetRange;
-
- public:
- using ValueType = T;
- using value_type = T;
-
- using iterator = T*;
- using const_iterator = const T*;
- using reverse_iterator = std::reverse_iterator<iterator>;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
- // Create an empty vector.
- inline Vector();
- // Create a vector containing the specified number of default-initialized
- // elements.
- inline explicit Vector(size_t);
- // Create a vector containing the specified number of elements, each of which
- // is copy initialized from the specified value.
- inline Vector(size_t, const T&);
-
- // Copying.
- Vector(const Vector&);
- template <size_t otherCapacity>
- explicit Vector(const Vector<T, otherCapacity, Allocator>&);
-
- Vector& operator=(const Vector&);
- template <size_t otherCapacity>
- Vector& operator=(const Vector<T, otherCapacity, Allocator>&);
-
- // Moving.
- Vector(Vector&&);
- Vector& operator=(Vector&&);
-
- // Construct with an initializer list. You can do e.g.
- // Vector<int> v({1, 2, 3});
- // or
- // v = {4, 5, 6};
- Vector(std::initializer_list<T> elements);
- Vector& operator=(std::initializer_list<T> elements);
-
- // Basic inquiry about the vector's state.
- //
- // capacity() is the maximum number of elements that the Vector can hold
- // without a reallocation. It can be zero.
- size_t size() const { return m_size; }
- size_t capacity() const { return Base::capacity(); }
- bool isEmpty() const { return !size(); }
-
- // at() and operator[]: Obtain the reference of the element that is located
- // at the given index. The reference may be invalidated on a reallocation.
- //
- // at() can be used in cases like:
- // pointerToVector->at(1);
- // instead of:
- // (*pointerToVector)[1];
- T& at(size_t i) {
- RELEASE_ASSERT(i < size());
- return Base::buffer()[i];
- }
- const T& at(size_t i) const {
- RELEASE_ASSERT(i < size());
- return Base::buffer()[i];
- }
-
- T& operator[](size_t i) { return at(i); }
- const T& operator[](size_t i) const { return at(i); }
-
- // Return a pointer to the front of the backing buffer. Those pointers get
- // invalidated on a reallocation.
- T* data() { return Base::buffer(); }
- const T* data() const { return Base::buffer(); }
-
- // Iterators and reverse iterators. They are invalidated on a reallocation.
- iterator begin() { return data(); }
- iterator end() { return begin() + m_size; }
- const_iterator begin() const { return data(); }
- const_iterator end() const { return begin() + m_size; }
-
- reverse_iterator rbegin() { return reverse_iterator(end()); }
- reverse_iterator rend() { return reverse_iterator(begin()); }
- const_reverse_iterator rbegin() const {
- return const_reverse_iterator(end());
- }
- const_reverse_iterator rend() const {
- return const_reverse_iterator(begin());
- }
-
- // Quick access to the first and the last element. It is invalid to call
- // these functions when the vector is empty.
- T& front() { return at(0); }
- const T& front() const { return at(0); }
- T& back() { return at(size() - 1); }
- const T& back() const { return at(size() - 1); }
-
- // Searching.
- //
- // Comparisons are done in terms of compareElement(), which is usually
- // operator==(). find() and reverseFind() returns an index of the element
- // that is found first. If no match is found, kNotFound will be returned.
- template <typename U>
- bool contains(const U&) const;
- template <typename U>
- size_t find(const U&) const;
- template <typename U>
- size_t reverseFind(const U&) const;
-
- // Resize the vector to the specified size.
- //
- // These three functions are essentially similar. They differ in that
- // (1) shrink() has a DCHECK to make sure the specified size is not more than
- // size(), and (2) grow() has a DCHECK to make sure the specified size is
- // not less than size().
- //
- // When a vector shrinks, the extra elements in the back will be destructed.
- // All the iterators pointing to a to-be-destructed element will be
- // invalidated.
- //
- // When a vector grows, new elements will be added in the back, and they
- // will be default-initialized. A reallocation may happen in this case.
- void shrink(size_t);
- void grow(size_t);
- void resize(size_t);
-
- // Increase the capacity of the vector to at least |newCapacity|. The
- // elements in the vector are not affected. This function does not shrink
- // the size of the backing buffer, even if |newCapacity| is small. This
- // function may cause a reallocation.
- void reserveCapacity(size_t newCapacity);
-
- // This is similar to reserveCapacity() but must be called immediately after
- // the vector is default-constructed.
- void reserveInitialCapacity(size_t initialCapacity);
-
- // Shrink the backing buffer so it can contain exactly |size()| elements.
- // This function may cause a reallocation.
- void shrinkToFit() { shrinkCapacity(size()); }
-
- // Shrink the backing buffer if at least 50% of the vector's capacity is
- // unused. If it shrinks, the new buffer contains roughly 25% of unused
- // space. This function may cause a reallocation.
- void shrinkToReasonableCapacity() {
- if (size() * 2 < capacity())
- shrinkCapacity(size() + size() / 4 + 1);
- }
-
- // Remove all the elements. This function actually releases the backing
- // buffer, thus any iterators will get invalidated (including begin()).
- void clear() { shrinkCapacity(0); }
-
- // Insertion to the back. All of these functions except uncheckedAppend() may
- // cause a reallocation.
- //
- // push_back(value)
- // Insert a single element to the back.
- // emplace_back(args...)
- // Insert a single element constructed as T(args...) to the back. The
- // element is constructed directly on the backing buffer with placement
- // new.
- // append(buffer, size)
- // appendVector(vector)
- // appendRange(begin, end)
- // Insert multiple elements represented by (1) |buffer| and |size|
- // (for append), (2) |vector| (for appendVector), or (3) a pair of
- // iterators (for appendRange) to the back. The elements will be copied.
- // uncheckedAppend(value)
- // Insert a single element like push_back(), but this function assumes
- // the vector has enough capacity such that it can store the new element
- // without a reallocation. Using this function could improve the
- // performance when you append many elements repeatedly.
- template <typename U>
- void push_back(U&&);
- template <typename... Args>
- T& emplace_back(Args&&...);
- ALWAYS_INLINE T& emplace_back() {
- grow(m_size + 1);
- return back();
- }
- template <typename U>
- void append(const U*, size_t);
- template <typename U, size_t otherCapacity, typename V>
- void appendVector(const Vector<U, otherCapacity, V>&);
- template <typename Iterator>
- void appendRange(Iterator begin, Iterator end);
- template <typename U>
- void uncheckedAppend(U&&);
-
- // Insertion to an arbitrary position. All of these functions will take
- // O(size())-time. All of the elements after |position| will be moved to
- // the new locations. |position| must be no more than size(). All of these
- // functions may cause a reallocation. In any case, all the iterators
- // pointing to an element after |position| will be invalidated.
- //
- // insert(position, value)
- // Insert a single element at |position|.
- // insert(position, buffer, size)
- // insert(position, vector)
- // Insert multiple elements represented by either |buffer| and |size|
- // or |vector| at |position|. The elements will be copied.
- //
- // TODO(yutak): Why not insertVector()?
- template <typename U>
- void insert(size_t position, U&&);
- template <typename U>
- void insert(size_t position, const U*, size_t);
- template <typename U, size_t otherCapacity, typename OtherAllocator>
- void insert(size_t position, const Vector<U, otherCapacity, OtherAllocator>&);
-
- // Insertion to the front. All of these functions will take O(size())-time.
- // All of the elements in the vector will be moved to the new locations.
- // All of these functions may cause a reallocation. In any case, all the
- // iterators pointing to any element in the vector will be invalidated.
- //
- // push_front(value)
- // Insert a single element to the front.
- // push_front(buffer, size)
- // prependVector(vector)
- // Insert multiple elements represented by either |buffer| and |size| or
- // |vector| to the front. The elements will be copied.
- template <typename U>
- void push_front(U&&);
- template <typename U>
- void push_front(const U*, size_t);
- template <typename U, size_t otherCapacity, typename OtherAllocator>
- void prependVector(const Vector<U, otherCapacity, OtherAllocator>&);
-
- // Remove an element or elements at the specified position. These functions
- // take O(size())-time. All of the elements after the removed ones will be
- // moved to the new locations. All the iterators pointing to any element
- // after |position| will be invalidated.
- void remove(size_t position);
- void remove(size_t position, size_t length);
-
- // Remove the last element. Unlike remove(), (1) this function is fast, and
- // (2) only iterators pointing to the last element will be invalidated. Other
- // references will remain valid.
- void pop_back() {
- DCHECK(!isEmpty());
- shrink(size() - 1);
- }
-
- // Filling the vector with the same value. If the vector has shrinked or
- // growed as a result of this call, those events may invalidate some
- // iterators. See comments for shrink() and grow().
- //
- // fill(value, size) will resize the Vector to |size|, and then copy-assign
- // or copy-initialize all the elements.
- //
- // fill(value) is a synonym for fill(value, size()).
- void fill(const T&, size_t);
- void fill(const T& val) { fill(val, size()); }
-
- // Swap two vectors quickly.
- void swap(Vector& other) {
- Base::swapVectorBuffer(other, OffsetRange(), OffsetRange());
- }
-
- // Reverse the contents.
- void reverse();
-
- // Maximum element count supported; allocating a vector
- // buffer with a larger count will fail.
- static size_t maxCapacity() {
- return Allocator::template maxElementCountInBackingStore<T>();
- }
-
- // Off-GC-heap vectors: Destructor should be called.
- // On-GC-heap vectors: Destructor should be called for inline buffers (if
- // any) but destructor shouldn't be called for vector backing since it is
- // managed by the traced GC heap.
- void finalize() {
- if (!INLINE_CAPACITY) {
- if (LIKELY(!Base::buffer()))
- return;
- }
- ANNOTATE_DELETE_BUFFER(begin(), capacity(), m_size);
- if (LIKELY(m_size) &&
- !(Allocator::isGarbageCollected && this->hasOutOfLineBuffer())) {
- TypeOperations::destruct(begin(), end());
- m_size = 0; // Partial protection against use-after-free.
- }
-
- Base::destruct();
- }
-
- void finalizeGarbageCollectedObject() { finalize(); }
-
- template <typename VisitorDispatcher>
- void trace(VisitorDispatcher);
-
- class GCForbiddenScope {
- STACK_ALLOCATED();
-
- public:
- GCForbiddenScope() { Allocator::enterGCForbiddenScope(); }
- ~GCForbiddenScope() { Allocator::leaveGCForbiddenScope(); }
- };
-
- protected:
- using Base::checkUnusedSlots;
- using Base::clearUnusedSlots;
-
- private:
- void expandCapacity(size_t newMinCapacity);
- T* expandCapacity(size_t newMinCapacity, T*);
- T* expandCapacity(size_t newMinCapacity, const T* data) {
- return expandCapacity(newMinCapacity, const_cast<T*>(data));
- }
-
- template <typename U>
- U* expandCapacity(size_t newMinCapacity, U*);
- void shrinkCapacity(size_t newCapacity);
- template <typename U>
- void appendSlowCase(U&&);
-
- using Base::m_size;
- using Base::buffer;
- using Base::swapVectorBuffer;
- using Base::allocateBuffer;
- using Base::allocationSize;
-};
-
-//
-// Vector out-of-line implementation
-//
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline Vector<T, inlineCapacity, Allocator>::Vector() {
- static_assert(!std::is_polymorphic<T>::value ||
- !VectorTraits<T>::canInitializeWithMemset,
- "Cannot initialize with memset if there is a vtable");
- static_assert(Allocator::isGarbageCollected ||
- !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value,
- "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
- "have trace methods into an off-heap Vector");
- static_assert(Allocator::isGarbageCollected ||
- !IsPointerToGarbageCollectedType<T>::value,
- "Cannot put raw pointers to garbage-collected classes into "
- "an off-heap Vector. Use HeapVector<Member<T>> instead.");
-
- ANNOTATE_NEW_BUFFER(begin(), capacity(), 0);
- m_size = 0;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size) : Base(size) {
- static_assert(!std::is_polymorphic<T>::value ||
- !VectorTraits<T>::canInitializeWithMemset,
- "Cannot initialize with memset if there is a vtable");
- static_assert(Allocator::isGarbageCollected ||
- !AllowsOnlyPlacementNew<T>::value || !IsTraceable<T>::value,
- "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
- "have trace methods into an off-heap Vector");
- static_assert(Allocator::isGarbageCollected ||
- !IsPointerToGarbageCollectedType<T>::value,
- "Cannot put raw pointers to garbage-collected classes into "
- "an off-heap Vector. Use HeapVector<Member<T>> instead.");
-
- ANNOTATE_NEW_BUFFER(begin(), capacity(), size);
- m_size = size;
- TypeOperations::initialize(begin(), end());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline Vector<T, inlineCapacity, Allocator>::Vector(size_t size, const T& val)
- : Base(size) {
- // TODO(yutak): Introduce these assertions. Some use sites call this function
- // in the context where T is an incomplete type.
- //
- // static_assert(!std::is_polymorphic<T>::value ||
- // !VectorTraits<T>::canInitializeWithMemset,
- // "Cannot initialize with memset if there is a vtable");
- // static_assert(Allocator::isGarbageCollected ||
- // !AllowsOnlyPlacementNew<T>::value ||
- // !IsTraceable<T>::value,
- // "Cannot put DISALLOW_NEW_EXCEPT_PLACEMENT_NEW objects that "
- // "have trace methods into an off-heap Vector");
- // static_assert(Allocator::isGarbageCollected ||
- // !IsPointerToGarbageCollectedType<T>::value,
- // "Cannot put raw pointers to garbage-collected classes into "
- // "an off-heap Vector. Use HeapVector<Member<T>> instead.");
-
- ANNOTATE_NEW_BUFFER(begin(), capacity(), size);
- m_size = size;
- TypeOperations::uninitializedFill(begin(), end(), val);
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>::Vector(const Vector& other)
- : Base(other.capacity()) {
- ANNOTATE_NEW_BUFFER(begin(), capacity(), other.size());
- m_size = other.size();
- TypeOperations::uninitializedCopy(other.begin(), other.end(), begin());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <size_t otherCapacity>
-Vector<T, inlineCapacity, Allocator>::Vector(
- const Vector<T, otherCapacity, Allocator>& other)
- : Base(other.capacity()) {
- ANNOTATE_NEW_BUFFER(begin(), capacity(), other.size());
- m_size = other.size();
- TypeOperations::uninitializedCopy(other.begin(), other.end(), begin());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
-operator=(const Vector<T, inlineCapacity, Allocator>& other) {
- if (UNLIKELY(&other == this))
- return *this;
-
- if (size() > other.size()) {
- shrink(other.size());
- } else if (other.size() > capacity()) {
- clear();
- reserveCapacity(other.size());
- DCHECK(begin());
- }
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, other.size());
- std::copy(other.begin(), other.begin() + size(), begin());
- TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end());
- m_size = other.size();
-
- return *this;
-}
-
-inline bool typelessPointersAreEqual(const void* a, const void* b) {
- return a == b;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <size_t otherCapacity>
-Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
-operator=(const Vector<T, otherCapacity, Allocator>& other) {
- // If the inline capacities match, we should call the more specific
- // template. If the inline capacities don't match, the two objects
- // shouldn't be allocated the same address.
- DCHECK(!typelessPointersAreEqual(&other, this));
-
- if (size() > other.size()) {
- shrink(other.size());
- } else if (other.size() > capacity()) {
- clear();
- reserveCapacity(other.size());
- DCHECK(begin());
- }
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, other.size());
- std::copy(other.begin(), other.begin() + size(), begin());
- TypeOperations::uninitializedCopy(other.begin() + size(), other.end(), end());
- m_size = other.size();
-
- return *this;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>::Vector(
- Vector<T, inlineCapacity, Allocator>&& other) {
- m_size = 0;
- // It's a little weird to implement a move constructor using swap but this
- // way we don't have to add a move constructor to VectorBuffer.
- swap(other);
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
-operator=(Vector<T, inlineCapacity, Allocator>&& other) {
- swap(other);
- return *this;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>::Vector(std::initializer_list<T> elements)
- : Base(elements.size()) {
- ANNOTATE_NEW_BUFFER(begin(), capacity(), elements.size());
- m_size = elements.size();
- TypeOperations::uninitializedCopy(elements.begin(), elements.end(), begin());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
-operator=(std::initializer_list<T> elements) {
- if (size() > elements.size()) {
- shrink(elements.size());
- } else if (elements.size() > capacity()) {
- clear();
- reserveCapacity(elements.size());
- DCHECK(begin());
- }
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, elements.size());
- std::copy(elements.begin(), elements.begin() + m_size, begin());
- TypeOperations::uninitializedCopy(elements.begin() + m_size, elements.end(),
- end());
- m_size = elements.size();
-
- return *this;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-bool Vector<T, inlineCapacity, Allocator>::contains(const U& value) const {
- return find(value) != kNotFound;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-size_t Vector<T, inlineCapacity, Allocator>::find(const U& value) const {
- const T* b = begin();
- const T* e = end();
- for (const T* iter = b; iter < e; ++iter) {
- if (TypeOperations::compareElement(*iter, value))
- return iter - b;
- }
- return kNotFound;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-size_t Vector<T, inlineCapacity, Allocator>::reverseFind(const U& value) const {
- const T* b = begin();
- const T* iter = end();
- while (iter > b) {
- --iter;
- if (TypeOperations::compareElement(*iter, value))
- return iter - b;
- }
- return kNotFound;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::fill(const T& val, size_t newSize) {
- if (size() > newSize) {
- shrink(newSize);
- } else if (newSize > capacity()) {
- clear();
- reserveCapacity(newSize);
- DCHECK(begin());
- }
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, newSize);
- std::fill(begin(), end(), val);
- TypeOperations::uninitializedFill(end(), begin() + newSize, val);
- m_size = newSize;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::expandCapacity(
- size_t newMinCapacity) {
- size_t oldCapacity = capacity();
- size_t expandedCapacity = oldCapacity;
- // We use a more aggressive expansion strategy for Vectors with inline
- // storage. This is because they are more likely to be on the stack, so the
- // risk of heap bloat is minimized. Furthermore, exceeding the inline
- // capacity limit is not supposed to happen in the common case and may
- // indicate a pathological condition or microbenchmark.
- if (INLINE_CAPACITY) {
- expandedCapacity *= 2;
- // Check for integer overflow, which could happen in the 32-bit build.
- RELEASE_ASSERT(expandedCapacity > oldCapacity);
- } else {
- // This cannot integer overflow.
- // On 64-bit, the "expanded" integer is 32-bit, and any encroachment
- // above 2^32 will fail allocation in allocateBuffer(). On 32-bit,
- // there's not enough address space to hold the old and new buffers. In
- // addition, our underlying allocator is supposed to always fail on >
- // (2^31 - 1) allocations.
- expandedCapacity += (expandedCapacity / 4) + 1;
- }
- reserveCapacity(std::max(
- newMinCapacity,
- std::max(static_cast<size_t>(kInitialVectorSize), expandedCapacity)));
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-T* Vector<T, inlineCapacity, Allocator>::expandCapacity(size_t newMinCapacity,
- T* ptr) {
- if (ptr < begin() || ptr >= end()) {
- expandCapacity(newMinCapacity);
- return ptr;
- }
- size_t index = ptr - begin();
- expandCapacity(newMinCapacity);
- return begin() + index;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-inline U* Vector<T, inlineCapacity, Allocator>::expandCapacity(
- size_t newMinCapacity,
- U* ptr) {
- expandCapacity(newMinCapacity);
- return ptr;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void Vector<T, inlineCapacity, Allocator>::resize(size_t size) {
- if (size <= m_size) {
- TypeOperations::destruct(begin() + size, end());
- clearUnusedSlots(begin() + size, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, size);
- } else {
- if (size > capacity())
- expandCapacity(size);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, size);
- TypeOperations::initialize(end(), begin() + size);
- }
-
- m_size = size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::shrink(size_t size) {
- DCHECK_LE(size, m_size);
- TypeOperations::destruct(begin() + size, end());
- clearUnusedSlots(begin() + size, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, size);
- m_size = size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::grow(size_t size) {
- DCHECK_GE(size, m_size);
- if (size > capacity())
- expandCapacity(size);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, size);
- TypeOperations::initialize(end(), begin() + size);
- m_size = size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::reserveCapacity(size_t newCapacity) {
- if (UNLIKELY(newCapacity <= capacity()))
- return;
- T* oldBuffer = begin();
- if (!oldBuffer) {
- Base::allocateBuffer(newCapacity);
- return;
- }
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
- size_t oldCapacity = capacity();
-#endif
- // The Allocator::isGarbageCollected check is not needed. The check is just
- // a static hint for a compiler to indicate that Base::expandBuffer returns
- // false if Allocator is a PartitionAllocator.
- if (Allocator::isGarbageCollected && Base::expandBuffer(newCapacity)) {
- ANNOTATE_CHANGE_CAPACITY(begin(), oldCapacity, m_size, capacity());
- return;
- }
- T* oldEnd = end();
- Base::allocateExpandedBuffer(newCapacity);
- ANNOTATE_NEW_BUFFER(begin(), capacity(), m_size);
- TypeOperations::move(oldBuffer, oldEnd, begin());
- clearUnusedSlots(oldBuffer, oldEnd);
- ANNOTATE_DELETE_BUFFER(oldBuffer, oldCapacity, m_size);
- Base::deallocateBuffer(oldBuffer);
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void Vector<T, inlineCapacity, Allocator>::reserveInitialCapacity(
- size_t initialCapacity) {
- DCHECK(!m_size);
- DCHECK(capacity() == INLINE_CAPACITY);
- if (initialCapacity > INLINE_CAPACITY) {
- ANNOTATE_DELETE_BUFFER(begin(), capacity(), m_size);
- Base::allocateBuffer(initialCapacity);
- ANNOTATE_NEW_BUFFER(begin(), capacity(), m_size);
- }
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-void Vector<T, inlineCapacity, Allocator>::shrinkCapacity(size_t newCapacity) {
- if (newCapacity >= capacity())
- return;
-
- if (newCapacity < size())
- shrink(newCapacity);
-
- T* oldBuffer = begin();
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
- size_t oldCapacity = capacity();
-#endif
- if (newCapacity > 0) {
- if (Base::shrinkBuffer(newCapacity)) {
- ANNOTATE_CHANGE_CAPACITY(begin(), oldCapacity, m_size, capacity());
- return;
- }
-
- T* oldEnd = end();
- Base::allocateBuffer(newCapacity);
- if (begin() != oldBuffer) {
- ANNOTATE_NEW_BUFFER(begin(), capacity(), m_size);
- TypeOperations::move(oldBuffer, oldEnd, begin());
- clearUnusedSlots(oldBuffer, oldEnd);
- ANNOTATE_DELETE_BUFFER(oldBuffer, oldCapacity, m_size);
- }
- } else {
- Base::resetBufferPointer();
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
- if (oldBuffer != begin()) {
- ANNOTATE_NEW_BUFFER(begin(), capacity(), m_size);
- ANNOTATE_DELETE_BUFFER(oldBuffer, oldCapacity, m_size);
- }
-#endif
- }
-
- Base::deallocateBuffer(oldBuffer);
-}
-
-// Templatizing these is better than just letting the conversion happen
-// implicitly, because for instance it allows a PassRefPtr to be appended to a
-// RefPtr vector without refcount thrash.
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::push_back(U&& val) {
- DCHECK(Allocator::isAllocationAllowed());
- if (LIKELY(size() != capacity())) {
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1);
- new (NotNull, end()) T(std::forward<U>(val));
- ++m_size;
- return;
- }
-
- appendSlowCase(std::forward<U>(val));
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename... Args>
-ALWAYS_INLINE T& Vector<T, inlineCapacity, Allocator>::emplace_back(
- Args&&... args) {
- DCHECK(Allocator::isAllocationAllowed());
- if (UNLIKELY(size() == capacity()))
- expandCapacity(size() + 1);
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1);
- T* t = new (NotNull, end()) T(std::forward<Args>(args)...);
- ++m_size;
- return *t;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-void Vector<T, inlineCapacity, Allocator>::append(const U* data,
- size_t dataSize) {
- DCHECK(Allocator::isAllocationAllowed());
- size_t newSize = m_size + dataSize;
- if (newSize > capacity()) {
- data = expandCapacity(newSize, data);
- DCHECK(begin());
- }
- RELEASE_ASSERT(newSize >= m_size);
- T* dest = end();
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, newSize);
- VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy(
- data, &data[dataSize], dest);
- m_size = newSize;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-NEVER_INLINE void Vector<T, inlineCapacity, Allocator>::appendSlowCase(
- U&& val) {
- DCHECK_EQ(size(), capacity());
-
- typename std::remove_reference<U>::type* ptr = &val;
- ptr = expandCapacity(size() + 1, ptr);
- DCHECK(begin());
-
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1);
- new (NotNull, end()) T(std::forward<U>(*ptr));
- ++m_size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U, size_t otherCapacity, typename OtherAllocator>
-inline void Vector<T, inlineCapacity, Allocator>::appendVector(
- const Vector<U, otherCapacity, OtherAllocator>& val) {
- append(val.begin(), val.size());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename Iterator>
-void Vector<T, inlineCapacity, Allocator>::appendRange(Iterator begin,
- Iterator end) {
- for (Iterator it = begin; it != end; ++it)
- push_back(*it);
-}
-
-// This version of append saves a branch in the case where you know that the
-// vector's capacity is large enough for the append to succeed.
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::uncheckedAppend(
- U&& val) {
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
- // Vectors in ASAN builds don't have inlineCapacity.
- push_back(std::forward<U>(val));
-#else
- DCHECK_LT(size(), capacity());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1);
- new (NotNull, end()) T(std::forward<U>(val));
- ++m_size;
-#endif
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position,
- U&& val) {
- DCHECK(Allocator::isAllocationAllowed());
- RELEASE_ASSERT(position <= size());
- typename std::remove_reference<U>::type* data = &val;
- if (size() == capacity()) {
- data = expandCapacity(size() + 1, data);
- DCHECK(begin());
- }
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1);
- T* spot = begin() + position;
- TypeOperations::moveOverlapping(spot, end(), spot + 1);
- new (NotNull, spot) T(std::forward<U>(*data));
- ++m_size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-void Vector<T, inlineCapacity, Allocator>::insert(size_t position,
- const U* data,
- size_t dataSize) {
- DCHECK(Allocator::isAllocationAllowed());
- RELEASE_ASSERT(position <= size());
- size_t newSize = m_size + dataSize;
- if (newSize > capacity()) {
- data = expandCapacity(newSize, data);
- DCHECK(begin());
- }
- RELEASE_ASSERT(newSize >= m_size);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, newSize);
- T* spot = begin() + position;
- TypeOperations::moveOverlapping(spot, end(), spot + dataSize);
- VectorCopier<VectorTraits<T>::canCopyWithMemcpy, T>::uninitializedCopy(
- data, &data[dataSize], spot);
- m_size = newSize;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U, size_t otherCapacity, typename OtherAllocator>
-inline void Vector<T, inlineCapacity, Allocator>::insert(
- size_t position,
- const Vector<U, otherCapacity, OtherAllocator>& val) {
- insert(position, val.begin(), val.size());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-inline void Vector<T, inlineCapacity, Allocator>::push_front(U&& val) {
- insert(0, std::forward<U>(val));
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U>
-void Vector<T, inlineCapacity, Allocator>::push_front(const U* data,
- size_t dataSize) {
- insert(0, data, dataSize);
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename U, size_t otherCapacity, typename OtherAllocator>
-inline void Vector<T, inlineCapacity, Allocator>::prependVector(
- const Vector<U, otherCapacity, OtherAllocator>& val) {
- insert(0, val.begin(), val.size());
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void Vector<T, inlineCapacity, Allocator>::remove(size_t position) {
- RELEASE_ASSERT(position < size());
- T* spot = begin() + position;
- spot->~T();
- TypeOperations::moveOverlapping(spot + 1, end(), spot);
- clearUnusedSlots(end() - 1, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size - 1);
- --m_size;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void Vector<T, inlineCapacity, Allocator>::remove(size_t position,
- size_t length) {
- SECURITY_DCHECK(position <= size());
- if (!length)
- return;
- RELEASE_ASSERT(position + length <= size());
- T* beginSpot = begin() + position;
- T* endSpot = beginSpot + length;
- TypeOperations::destruct(beginSpot, endSpot);
- TypeOperations::moveOverlapping(endSpot, end(), beginSpot);
- clearUnusedSlots(end() - length, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size - length);
- m_size -= length;
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void Vector<T, inlineCapacity, Allocator>::reverse() {
- for (size_t i = 0; i < m_size / 2; ++i)
- std::swap(at(i), at(m_size - 1 - i));
-}
-
-template <typename T, size_t inlineCapacity, typename Allocator>
-inline void swap(Vector<T, inlineCapacity, Allocator>& a,
- Vector<T, inlineCapacity, Allocator>& b) {
- a.swap(b);
-}
-
-template <typename T,
- size_t inlineCapacityA,
- size_t inlineCapacityB,
- typename Allocator>
-bool operator==(const Vector<T, inlineCapacityA, Allocator>& a,
- const Vector<T, inlineCapacityB, Allocator>& b) {
- if (a.size() != b.size())
- return false;
- if (a.isEmpty())
- return true;
- return VectorTypeOperations<T>::compare(a.data(), b.data(), a.size());
-}
-
-template <typename T,
- size_t inlineCapacityA,
- size_t inlineCapacityB,
- typename Allocator>
-inline bool operator!=(const Vector<T, inlineCapacityA, Allocator>& a,
- const Vector<T, inlineCapacityB, Allocator>& b) {
- return !(a == b);
-}
-
-// This is only called if the allocator is a HeapAllocator. It is used when
-// visiting during a tracing GC.
-template <typename T, size_t inlineCapacity, typename Allocator>
-template <typename VisitorDispatcher>
-void Vector<T, inlineCapacity, Allocator>::trace(VisitorDispatcher visitor) {
- DCHECK(Allocator::isGarbageCollected) << "Garbage collector must be enabled.";
- if (!buffer())
- return;
- if (this->hasOutOfLineBuffer()) {
- // This is a performance optimization for a case where the buffer has
- // been already traced by somewhere. This can happen if the conservative
- // scanning traced an on-stack (false-positive or real) pointer to the
- // HeapVector, and then visitor->trace() traces the HeapVector.
- if (Allocator::isHeapObjectAlive(buffer()))
- return;
- Allocator::markNoTracing(visitor, buffer());
- Allocator::registerBackingStoreReference(visitor, Base::bufferSlot());
- }
- const T* bufferBegin = buffer();
- const T* bufferEnd = buffer() + size();
- if (IsTraceableInCollectionTrait<VectorTraits<T>>::value) {
- for (const T* bufferEntry = bufferBegin; bufferEntry != bufferEnd;
- bufferEntry++)
- Allocator::template trace<VisitorDispatcher, T, VectorTraits<T>>(
- visitor, *const_cast<T*>(bufferEntry));
- checkUnusedSlots(buffer() + size(), buffer() + capacity());
- }
-}
-
-} // namespace WTF
-
-using WTF::Vector;
-
-#endif // WTF_Vector_h
+// The contents of this header was moved to platform/wtf as part of
+// WTF migration project. See the following post for details:
+// https://groups.google.com/a/chromium.org/d/msg/blink-dev/tLdAZCTlcAA/bYXVT8gYCAAJ
« no previous file with comments | « third_party/WebKit/Source/wtf/PrintStream.h ('k') | third_party/WebKit/Source/wtf/VectorTraits.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698