1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_BASE_SMALL_VECTOR_H_ 6#define V8_BASE_SMALL_VECTOR_H_ 7 8#include <algorithm> 9#include <type_traits> 10#include <utility> 11 12#include "src/base/bits.h" 13#include "src/base/macros.h" 14 15namespace v8 { 16namespace base { 17 18// Minimal SmallVector implementation. Uses inline storage first, switches to 19// dynamic storage when it overflows. 20template <typename T, size_t kSize, typename Allocator = std::allocator<T>> 21class SmallVector { 22 // Currently only support trivially copyable and trivially destructible data 23 // types, as it uses memcpy to copy elements and never calls destructors. 24 ASSERT_TRIVIALLY_COPYABLE(T); 25 STATIC_ASSERT(std::is_trivially_destructible<T>::value); 26 27 public: 28 static constexpr size_t kInlineSize = kSize; 29 30 explicit SmallVector(const Allocator& allocator = Allocator()) 31 : allocator_(allocator) {} 32 explicit SmallVector(size_t size, const Allocator& allocator = Allocator()) 33 : allocator_(allocator) { 34 resize_no_init(size); 35 } 36 SmallVector(const SmallVector& other, 37 const Allocator& allocator = Allocator()) V8_NOEXCEPT 38 : allocator_(allocator) { 39 *this = other; 40 } 41 SmallVector(SmallVector&& other, 42 const Allocator& allocator = Allocator()) V8_NOEXCEPT 43 : allocator_(allocator) { 44 *this = std::move(other); 45 } 46 SmallVector(std::initializer_list<T> init, 47 const Allocator& allocator = Allocator()) 48 : allocator_(allocator) { 49 resize_no_init(init.size()); 50 memcpy(begin_, init.begin(), sizeof(T) * init.size()); 51 } 52 53 ~SmallVector() { 54 if (is_big()) FreeDynamicStorage(); 55 } 56 57 SmallVector& operator=(const SmallVector& other) V8_NOEXCEPT { 58 if (this == &other) return *this; 59 size_t other_size = other.size(); 60 if (capacity() < other_size) { 61 // Create large-enough heap-allocated storage. 62 if (is_big()) FreeDynamicStorage(); 63 begin_ = AllocateDynamicStorage(other_size); 64 end_of_storage_ = begin_ + other_size; 65 } 66 memcpy(begin_, other.begin_, sizeof(T) * other_size); 67 end_ = begin_ + other_size; 68 return *this; 69 } 70 71 SmallVector& operator=(SmallVector&& other) V8_NOEXCEPT { 72 if (this == &other) return *this; 73 if (other.is_big()) { 74 if (is_big()) FreeDynamicStorage(); 75 begin_ = other.begin_; 76 end_ = other.end_; 77 end_of_storage_ = other.end_of_storage_; 78 other.reset_to_inline_storage(); 79 } else { 80 DCHECK_GE(capacity(), other.size()); // Sanity check. 81 size_t other_size = other.size(); 82 memcpy(begin_, other.begin_, sizeof(T) * other_size); 83 end_ = begin_ + other_size; 84 } 85 return *this; 86 } 87 88 T* data() { return begin_; } 89 const T* data() const { return begin_; } 90 91 T* begin() { return begin_; } 92 const T* begin() const { return begin_; } 93 94 T* end() { return end_; } 95 const T* end() const { return end_; } 96 97 size_t size() const { return end_ - begin_; } 98 bool empty() const { return end_ == begin_; } 99 size_t capacity() const { return end_of_storage_ - begin_; } 100 101 T& back() { 102 DCHECK_NE(0, size()); 103 return end_[-1]; 104 } 105 const T& back() const { 106 DCHECK_NE(0, size()); 107 return end_[-1]; 108 } 109 110 T& operator[](size_t index) { 111 DCHECK_GT(size(), index); 112 return begin_[index]; 113 } 114 115 const T& at(size_t index) const { 116 DCHECK_GT(size(), index); 117 return begin_[index]; 118 } 119 120 const T& operator[](size_t index) const { return at(index); } 121 122 template <typename... Args> 123 void emplace_back(Args&&... args) { 124 T* end = end_; 125 if (V8_UNLIKELY(end == end_of_storage_)) end = Grow(); 126 new (end) T(std::forward<Args>(args)...); 127 end_ = end + 1; 128 } 129 130 void pop_back(size_t count = 1) { 131 DCHECK_GE(size(), count); 132 end_ -= count; 133 } 134 135 void resize_no_init(size_t new_size) { 136 // Resizing without initialization is safe if T is trivially copyable. 137 ASSERT_TRIVIALLY_COPYABLE(T); 138 if (new_size > capacity()) Grow(new_size); 139 end_ = begin_ + new_size; 140 } 141 142 // Clear without reverting back to inline storage. 143 void clear() { end_ = begin_; } 144 145 private: 146 V8_NO_UNIQUE_ADDRESS Allocator allocator_; 147 148 T* begin_ = inline_storage_begin(); 149 T* end_ = begin_; 150 T* end_of_storage_ = begin_ + kInlineSize; 151 typename std::aligned_storage<sizeof(T) * kInlineSize, alignof(T)>::type 152 inline_storage_; 153 154 // Grows the backing store by a factor of two. Returns the new end of the used 155 // storage (this reduces binary size). 156 V8_NOINLINE T* Grow() { return Grow(0); } 157 158 // Grows the backing store by a factor of two, and at least to {min_capacity}. 159 V8_NOINLINE T* Grow(size_t min_capacity) { 160 size_t in_use = end_ - begin_; 161 size_t new_capacity = 162 base::bits::RoundUpToPowerOfTwo(std::max(min_capacity, 2 * capacity())); 163 T* new_storage = AllocateDynamicStorage(new_capacity); 164 if (new_storage == nullptr) { 165 // Should be: V8::FatalProcessOutOfMemory, but we don't include V8 from 166 // base. The message is intentionally the same as FatalProcessOutOfMemory 167 // since that will help fuzzers and chromecrash to categorize such 168 // crashes appropriately. 169 FATAL("Fatal process out of memory: base::SmallVector::Grow"); 170 } 171 memcpy(new_storage, begin_, sizeof(T) * in_use); 172 if (is_big()) FreeDynamicStorage(); 173 begin_ = new_storage; 174 end_ = new_storage + in_use; 175 end_of_storage_ = new_storage + new_capacity; 176 return end_; 177 } 178 179 T* AllocateDynamicStorage(size_t number_of_elements) { 180 return allocator_.allocate(number_of_elements); 181 } 182 183 void FreeDynamicStorage() { 184 DCHECK(is_big()); 185 allocator_.deallocate(begin_, end_of_storage_ - begin_); 186 } 187 188 // Clear and go back to inline storage. Dynamic storage is *not* freed. For 189 // internal use only. 190 void reset_to_inline_storage() { 191 begin_ = inline_storage_begin(); 192 end_ = begin_; 193 end_of_storage_ = begin_ + kInlineSize; 194 } 195 196 bool is_big() const { return begin_ != inline_storage_begin(); } 197 198 T* inline_storage_begin() { return reinterpret_cast<T*>(&inline_storage_); } 199 const T* inline_storage_begin() const { 200 return reinterpret_cast<const T*>(&inline_storage_); 201 } 202}; 203 204} // namespace base 205} // namespace v8 206 207#endif // V8_BASE_SMALL_VECTOR_H_ 208