xref: /third_party/node/deps/v8/src/base/small-vector.h (revision 1cb0ef41)
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