• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 namespace v8 {
16 namespace base {
17 
18 // Minimal SmallVector implementation. Uses inline storage first, switches to
19 // dynamic storage when it overflows.
20 template <typename T, size_t kSize, typename Allocator = std::allocator<T>>
21 class 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())
allocator_(allocator)31       : allocator_(allocator) {}
32   explicit SmallVector(size_t size, const Allocator& allocator = Allocator())
allocator_(allocator)33       : allocator_(allocator) {
34     resize_no_init(size);
35   }
36   SmallVector(const SmallVector& other,
37               const Allocator& allocator = Allocator()) V8_NOEXCEPT
allocator_(allocator)38       : allocator_(allocator) {
39     *this = other;
40   }
41   SmallVector(SmallVector&& other,
42               const Allocator& allocator = Allocator()) V8_NOEXCEPT
allocator_(allocator)43       : allocator_(allocator) {
44     *this = std::move(other);
45   }
46   SmallVector(std::initializer_list<T> init,
47               const Allocator& allocator = Allocator())
allocator_(allocator)48       : allocator_(allocator) {
49     resize_no_init(init.size());
50     memcpy(begin_, init.begin(), sizeof(T) * init.size());
51   }
52 
~SmallVector()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 
data()88   T* data() { return begin_; }
data()89   const T* data() const { return begin_; }
90 
begin()91   T* begin() { return begin_; }
begin()92   const T* begin() const { return begin_; }
93 
end()94   T* end() { return end_; }
end()95   const T* end() const { return end_; }
96 
size()97   size_t size() const { return end_ - begin_; }
empty()98   bool empty() const { return end_ == begin_; }
capacity()99   size_t capacity() const { return end_of_storage_ - begin_; }
100 
back()101   T& back() {
102     DCHECK_NE(0, size());
103     return end_[-1];
104   }
back()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 
at(size_t index)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>
emplace_back(Args &&...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 
resize_no_init(size_t new_size)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.
clear()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).
Grow()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}.
Grow(size_t 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 
AllocateDynamicStorage(size_t number_of_elements)179   T* AllocateDynamicStorage(size_t number_of_elements) {
180     return allocator_.allocate(number_of_elements);
181   }
182 
FreeDynamicStorage()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.
reset_to_inline_storage()190   void reset_to_inline_storage() {
191     begin_ = inline_storage_begin();
192     end_ = begin_;
193     end_of_storage_ = begin_ + kInlineSize;
194   }
195 
is_big()196   bool is_big() const { return begin_ != inline_storage_begin(); }
197 
inline_storage_begin()198   T* inline_storage_begin() { return reinterpret_cast<T*>(&inline_storage_); }
inline_storage_begin()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