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 |