v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
small-vector.h
Go to the documentation of this file.
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#include "src/base/vector.h"
15
16namespace v8 {
17namespace base {
18
19// Minimal SmallVector implementation. Uses inline storage first, switches to
20// dynamic storage when it overflows.
21template <typename T, size_t kSize, typename Allocator = std::allocator<T>>
23 public:
24 static constexpr size_t kInlineSize = kSize;
25 using value_type = T;
26
27 SmallVector() = default;
28 explicit SmallVector(const Allocator& allocator) : allocator_(allocator) {}
29 explicit V8_INLINE SmallVector(size_t size,
30 const Allocator& allocator = Allocator())
31 : allocator_(allocator) {
32 resize(size);
33 }
34 explicit V8_INLINE SmallVector(size_t size, const T& initial_value,
35 const Allocator& allocator = Allocator())
36 : allocator_(allocator) {
37 resize(size, initial_value);
38 }
40 : allocator_(other.allocator_) {
41 *this = other;
42 }
43 SmallVector(const SmallVector& other, const Allocator& allocator) V8_NOEXCEPT
44 : allocator_(allocator) {
45 *this = other;
46 }
48 : allocator_(std::move(other.allocator_)) {
49 *this = std::move(other);
50 }
51 SmallVector(SmallVector&& other, const Allocator& allocator) V8_NOEXCEPT
52 : allocator_(allocator) {
53 *this = std::move(other);
54 }
55 V8_INLINE SmallVector(std::initializer_list<T> init,
56 const Allocator& allocator = Allocator())
57 : allocator_(allocator) {
58 if (init.size() > capacity()) Grow(init.size());
59 DCHECK_GE(capacity(), init.size()); // Sanity check.
60 std::uninitialized_move(init.begin(), init.end(), begin_);
61 end_ = begin_ + init.size();
62 }
64 const Allocator& allocator = Allocator())
65 : allocator_(allocator) {
66 if (init.size() > capacity()) Grow(init.size());
67 DCHECK_GE(capacity(), init.size()); // Sanity check.
68 std::uninitialized_copy(init.begin(), init.end(), begin_);
69 end_ = begin_ + init.size();
70 }
71
73
75 if (this == &other) return *this;
76 size_t other_size = other.size();
77 if (capacity() < other_size) {
78 // Create large-enough heap-allocated storage.
80 begin_ = AllocateDynamicStorage(other_size);
81 end_of_storage_ = begin_ + other_size;
82 std::uninitialized_copy(other.begin_, other.end_, begin_);
83 } else if constexpr (kHasTrivialElement) {
84 std::copy(other.begin_, other.end_, begin_);
85 } else {
86 ptrdiff_t to_copy =
87 std::min(static_cast<ptrdiff_t>(other_size), end_ - begin_);
88 std::copy(other.begin_, other.begin_ + to_copy, begin_);
89 if (other.begin_ + to_copy < other.end_) {
90 std::uninitialized_copy(other.begin_ + to_copy, other.end_,
91 begin_ + to_copy);
92 } else {
93 std::destroy_n(begin_ + to_copy, size() - to_copy);
94 }
95 }
96 end_ = begin_ + other_size;
97 return *this;
98 }
99
101 if (this == &other) return *this;
102 if (other.is_big()) {
103 FreeStorage();
104 begin_ = other.begin_;
105 end_ = other.end_;
106 end_of_storage_ = other.end_of_storage_;
107 } else {
108 DCHECK_GE(capacity(), other.size()); // Sanity check.
109 size_t other_size = other.size();
110 if constexpr (kHasTrivialElement) {
111 std::move(other.begin_, other.end_, begin_);
112 } else {
113 ptrdiff_t to_move =
114 std::min(static_cast<ptrdiff_t>(other_size), end_ - begin_);
115 std::move(other.begin_, other.begin_ + to_move, begin_);
116 if (other.begin_ + to_move < other.end_) {
117 std::uninitialized_move(other.begin_ + to_move, other.end_,
118 begin_ + to_move);
119 } else {
120 std::destroy_n(begin_ + to_move, size() - to_move);
121 }
122 }
123 end_ = begin_ + other_size;
124 }
125 other.reset_to_inline_storage();
126 return *this;
127 }
128
129 T* data() { return begin_; }
130 const T* data() const { return begin_; }
131
132 T* begin() { return begin_; }
133 const T* begin() const { return begin_; }
134
135 T* end() { return end_; }
136 const T* end() const { return end_; }
137
138 auto rbegin() { return std::make_reverse_iterator(end_); }
139 auto rbegin() const { return std::make_reverse_iterator(end_); }
140
141 auto rend() { return std::make_reverse_iterator(begin_); }
142 auto rend() const { return std::make_reverse_iterator(begin_); }
143
144 size_t size() const { return end_ - begin_; }
145 bool empty() const { return end_ == begin_; }
146 size_t capacity() const { return end_of_storage_ - begin_; }
147
148 T& front() {
149 DCHECK_NE(0, size());
150 return begin_[0];
151 }
152 const T& front() const {
153 DCHECK_NE(0, size());
154 return begin_[0];
155 }
156
157 T& back() {
158 DCHECK_NE(0, size());
159 return end_[-1];
160 }
161 const T& back() const {
162 DCHECK_NE(0, size());
163 return end_[-1];
164 }
165
166 T& at(size_t index) {
167 DCHECK_GT(size(), index);
168 return begin_[index];
169 }
170
171 T& operator[](size_t index) {
172 DCHECK_GT(size(), index);
173 return begin_[index];
174 }
175
176 const T& at(size_t index) const {
177 DCHECK_GT(size(), index);
178 return begin_[index];
179 }
180
181 const T& operator[](size_t index) const { return at(index); }
182
183 template <typename... Args>
184 void emplace_back(Args&&... args) {
186 void* storage = end_;
187 end_ += 1;
188 new (storage) T(std::forward<Args>(args)...);
189 }
190
191 void push_back(T x) { emplace_back(std::move(x)); }
192
193 void pop_back(size_t count = 1) {
194 DCHECK_GE(size(), count);
195 end_ -= count;
196 std::destroy_n(end_, count);
197 }
198
199 T* insert(T* pos, const T& value) {
200 return insert(pos, static_cast<size_t>(1), value);
201 }
202 T* insert(T* pos, size_t count, const T& value) {
204 size_t offset = pos - begin_;
205 size_t old_size = size();
206 resize(old_size + count);
207 pos = begin_ + offset;
208 T* old_end = begin_ + old_size;
209 DCHECK_LE(old_end, end_);
210 std::move_backward(pos, old_end, end_);
211 std::fill_n(pos, count, value);
212 return pos;
213 }
214 template <typename It>
215 T* insert(T* pos, It begin, It end) {
217 size_t offset = pos - begin_;
218 size_t count = std::distance(begin, end);
219 size_t old_size = size();
220 resize(old_size + count);
221 pos = begin_ + offset;
222 T* old_end = begin_ + old_size;
223 DCHECK_LE(old_end, end_);
224 std::move_backward(pos, old_end, end_);
225 std::copy(begin, end, pos);
226 return pos;
227 }
228
229 T* insert(T* pos, std::initializer_list<T> values) {
230 return insert(pos, values.begin(), values.end());
231 }
232
233 void erase(T* erase_start) {
234 DCHECK_GE(erase_start, begin_);
235 DCHECK_LE(erase_start, end_);
236 ptrdiff_t count = end_ - erase_start;
237 end_ = erase_start;
238 std::destroy_n(end_, count);
239 }
240
241 void resize(size_t new_size) {
242 if (new_size > capacity()) Grow(new_size);
243 T* new_end = begin_ + new_size;
244 if constexpr (!kHasTrivialElement) {
245 if (new_end > end_) {
246 std::uninitialized_default_construct(end_, new_end);
247 } else {
248 std::destroy_n(new_end, end_ - new_end);
249 }
250 }
251 end_ = new_end;
252 }
253
254 void resize(size_t new_size, const T& initial_value) {
255 if (new_size > capacity()) Grow(new_size);
256 T* new_end = begin_ + new_size;
257 if (new_end > end_) {
258 std::uninitialized_fill(end_, new_end, initial_value);
259 } else {
260 std::destroy_n(new_end, end_ - new_end);
261 }
262 end_ = new_end;
263 }
264
265 void reserve(size_t new_capacity) {
266 if (new_capacity > capacity()) Grow(new_capacity);
267 }
268
269 // Clear without reverting back to inline storage.
270 void clear() {
271 std::destroy_n(begin_, end_ - begin_);
272 end_ = begin_;
273 }
274
275 Allocator get_allocator() const { return allocator_; }
276
277 private:
278 // Grows the backing store by a factor of two. Returns the new end of the used
279 // storage (this reduces binary size).
281
282 // Grows the backing store by a factor of two, and at least to {min_capacity}.
283 V8_NOINLINE V8_PRESERVE_MOST void Grow(size_t min_capacity) {
284 size_t in_use = end_ - begin_;
285 size_t new_capacity =
286 base::bits::RoundUpToPowerOfTwo(std::max(min_capacity, 2 * capacity()));
287 T* new_storage = AllocateDynamicStorage(new_capacity);
288 if (new_storage == nullptr) {
289 FatalOOM(OOMType::kProcess, "base::SmallVector::Grow");
290 }
291 std::uninitialized_move(begin_, end_, new_storage);
292 FreeStorage();
293 begin_ = new_storage;
294 end_ = new_storage + in_use;
295 end_of_storage_ = new_storage + new_capacity;
296 }
297
298 T* AllocateDynamicStorage(size_t number_of_elements) {
299 return allocator_.allocate(number_of_elements);
300 }
301
303 std::destroy_n(begin_, end_ - begin_);
304 if (is_big()) allocator_.deallocate(begin_, end_of_storage_ - begin_);
305 }
306
307 // Clear and go back to inline storage. Dynamic storage is *not* freed. For
308 // internal use only.
310 if constexpr (!kHasTrivialElement) {
311 if (!is_big()) std::destroy_n(begin_, end_ - begin_);
312 }
314 end_ = begin_;
316 }
317
318 bool is_big() const { return begin_ != inline_storage_begin(); }
319
320 T* inline_storage_begin() { return reinterpret_cast<T*>(inline_storage_); }
321 const T* inline_storage_begin() const {
322 return reinterpret_cast<const T*>(inline_storage_);
323 }
324
326
327 // Invariants:
328 // 1. The elements in the range between `begin_` (included) and `end_` (not
329 // included) will be initialized at all times.
330 // 2. All other elements outside the range, both in the inline storage and in
331 // the dynamic storage (if it exists), will be uninitialized at all times.
332
336 alignas(T) char inline_storage_[sizeof(T) * kInlineSize];
337
338 static constexpr bool kHasTrivialElement =
340};
341
342} // namespace base
343} // namespace v8
344
345#endif // V8_BASE_SMALL_VECTOR_H_
#define T
SourcePosition pos
SmallVector(const SmallVector &other, const Allocator &allocator) V8_NOEXCEPT
size_t capacity() const
void erase(T *erase_start)
V8_INLINE SmallVector(size_t size, const T &initial_value, const Allocator &allocator=Allocator())
V8_NOINLINE V8_PRESERVE_MOST void Grow(size_t min_capacity)
V8_INLINE SmallVector(size_t size, const Allocator &allocator=Allocator())
char inline_storage_[sizeof(T) *kInlineSize]
T * insert(T *pos, It begin, It end)
void pop_back(size_t count=1)
T * insert(T *pos, std::initializer_list< T > values)
size_t size() const
void resize(size_t new_size, const T &initial_value)
const T * end() const
T * insert(T *pos, const T &value)
const T * data() const
const T * begin() const
void emplace_back(Args &&... args)
V8_NOINLINE V8_PRESERVE_MOST void Grow()
static constexpr size_t kInlineSize
const T & back() const
T & at(size_t index)
const T & operator[](size_t index) const
SmallVector & operator=(SmallVector &&other) V8_NOEXCEPT
SmallVector(const Allocator &allocator)
V8_INLINE SmallVector(base::Vector< const T > init, const Allocator &allocator=Allocator())
const T * inline_storage_begin() const
SmallVector(SmallVector &&other) V8_NOEXCEPT
SmallVector(SmallVector &&other, const Allocator &allocator) V8_NOEXCEPT
const T & front() const
V8_NO_UNIQUE_ADDRESS Allocator allocator_
V8_NOINLINE V8_PRESERVE_MOST void FreeStorage()
T * insert(T *pos, size_t count, const T &value)
Allocator get_allocator() const
T * AllocateDynamicStorage(size_t number_of_elements)
void reserve(size_t new_capacity)
T & operator[](size_t index)
static constexpr bool kHasTrivialElement
SmallVector(const SmallVector &other) V8_NOEXCEPT
const T & at(size_t index) const
V8_INLINE SmallVector(std::initializer_list< T > init, const Allocator &allocator=Allocator())
void resize(size_t new_size)
SmallVector & operator=(const SmallVector &other) V8_NOEXCEPT
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
uint32_t count
OptionalOpIndex index
int32_t offset
int x
constexpr size_t RoundUpToPowerOfTwo(size_t value)
Definition bits.h:252
void FatalOOM(OOMType type, const char *msg)
Definition logging.cc:80
V8_BASE_EXPORT int const char va_list args
Definition strings.h:23
#define V8_NOEXCEPT
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define V8_NO_UNIQUE_ADDRESS
Definition v8config.h:722
#define V8_INLINE
Definition v8config.h:500
#define V8_UNLIKELY(condition)
Definition v8config.h:660
#define V8_NOINLINE
Definition v8config.h:586
#define V8_PRESERVE_MOST
Definition v8config.h:598