small-vector.h 5.12 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2018 the V8 project 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 V8_BASE_SMALL_VECTOR_H_
#define V8_BASE_SMALL_VECTOR_H_

8
#include <algorithm>
9
#include <type_traits>
10
#include <utility>
11 12 13

#include "src/base/bits.h"
#include "src/base/macros.h"
14
#include "src/base/platform/wrappers.h"
15 16 17 18 19 20

namespace v8 {
namespace base {

// Minimal SmallVector implementation. Uses inline storage first, switches to
// malloc when it overflows.
21
template <typename T, size_t kSize>
22 23 24 25 26 27 28
class SmallVector {
  // Currently only support trivially copyable and trivially destructible data
  // types, as it uses memcpy to copy elements and never calls destructors.
  ASSERT_TRIVIALLY_COPYABLE(T);
  STATIC_ASSERT(std::is_trivially_destructible<T>::value);

 public:
29 30
  static constexpr size_t kInlineSize = kSize;

31
  SmallVector() = default;
32
  explicit SmallVector(size_t size) { resize_no_init(size); }
33 34
  SmallVector(const SmallVector& other) V8_NOEXCEPT { *this = other; }
  SmallVector(SmallVector&& other) V8_NOEXCEPT { *this = std::move(other); }
35 36
  SmallVector(std::initializer_list<T> init) {
    resize_no_init(init.size());
37
    base::Memcpy(begin_, init.begin(), sizeof(T) * init.size());
38
  }
39

40
  ~SmallVector() {
41
    if (is_big()) base::Free(begin_);
42 43
  }

44 45 46 47 48
  SmallVector& operator=(const SmallVector& other) V8_NOEXCEPT {
    if (this == &other) return *this;
    size_t other_size = other.size();
    if (capacity() < other_size) {
      // Create large-enough heap-allocated storage.
49 50
      if (is_big()) base::Free(begin_);
      begin_ = reinterpret_cast<T*>(base::Malloc(sizeof(T) * other_size));
51 52
      end_of_storage_ = begin_ + other_size;
    }
53
    base::Memcpy(begin_, other.begin_, sizeof(T) * other_size);
54 55 56 57 58 59 60
    end_ = begin_ + other_size;
    return *this;
  }

  SmallVector& operator=(SmallVector&& other) V8_NOEXCEPT {
    if (this == &other) return *this;
    if (other.is_big()) {
61
      if (is_big()) base::Free(begin_);
62 63 64 65 66 67 68
      begin_ = other.begin_;
      end_ = other.end_;
      end_of_storage_ = other.end_of_storage_;
      other.reset();
    } else {
      DCHECK_GE(capacity(), other.size());  // Sanity check.
      size_t other_size = other.size();
69
      base::Memcpy(begin_, other.begin_, sizeof(T) * other_size);
70 71 72 73 74
      end_ = begin_ + other_size;
    }
    return *this;
  }

75 76 77 78 79 80 81 82 83
  T* data() { return begin_; }
  const T* data() const { return begin_; }

  T* begin() { return begin_; }
  const T* begin() const { return begin_; }

  T* end() { return end_; }
  const T* end() const { return end_; }

84 85
  size_t size() const { return end_ - begin_; }
  bool empty() const { return end_ == begin_; }
86
  size_t capacity() const { return end_of_storage_ - begin_; }
87 88 89 90 91

  T& back() {
    DCHECK_NE(0, size());
    return end_[-1];
  }
92 93 94 95
  const T& back() const {
    DCHECK_NE(0, size());
    return end_[-1];
  }
96

97 98 99 100 101
  T& operator[](size_t index) {
    DCHECK_GT(size(), index);
    return begin_[index];
  }

102
  const T& at(size_t index) const {
103 104 105 106
    DCHECK_GT(size(), index);
    return begin_[index];
  }

107 108
  const T& operator[](size_t index) const { return at(index); }

109 110
  template <typename... Args>
  void emplace_back(Args&&... args) {
111 112 113 114
    T* end = end_;
    if (V8_UNLIKELY(end == end_of_storage_)) end = Grow();
    new (end) T(std::forward<Args>(args)...);
    end_ = end + 1;
115 116
  }

117
  void pop_back(size_t count = 1) {
118 119 120 121
    DCHECK_GE(size(), count);
    end_ -= count;
  }

122 123 124 125 126 127 128 129
  void resize_no_init(size_t new_size) {
    // Resizing without initialization is safe if T is trivially copyable.
    ASSERT_TRIVIALLY_COPYABLE(T);
    if (new_size > capacity()) Grow(new_size);
    end_ = begin_ + new_size;
  }

  // Clear without freeing any storage.
130 131
  void clear() { end_ = begin_; }

132 133 134 135 136 137 138
  // Clear and go back to inline storage.
  void reset() {
    begin_ = inline_storage_begin();
    end_ = begin_;
    end_of_storage_ = begin_ + kInlineSize;
  }

139 140 141 142 143 144 145
 private:
  T* begin_ = inline_storage_begin();
  T* end_ = begin_;
  T* end_of_storage_ = begin_ + kInlineSize;
  typename std::aligned_storage<sizeof(T) * kInlineSize, alignof(T)>::type
      inline_storage_;

146 147 148 149 150 151
  // Grows the backing store by a factor of two. Returns the new end of the used
  // storage (this reduces binary size).
  V8_NOINLINE T* Grow() { return Grow(0); }

  // Grows the backing store by a factor of two, and at least to {min_capacity}.
  V8_NOINLINE T* Grow(size_t min_capacity) {
152
    size_t in_use = end_ - begin_;
153 154
    size_t new_capacity =
        base::bits::RoundUpToPowerOfTwo(std::max(min_capacity, 2 * capacity()));
155 156 157 158
    T* new_storage =
        reinterpret_cast<T*>(base::Malloc(sizeof(T) * new_capacity));
    base::Memcpy(new_storage, begin_, sizeof(T) * in_use);
    if (is_big()) base::Free(begin_);
159 160 161
    begin_ = new_storage;
    end_ = new_storage + in_use;
    end_of_storage_ = new_storage + new_capacity;
162
    return end_;
163 164 165 166 167 168 169 170 171 172 173 174 175 176
  }

  bool is_big() const { return begin_ != inline_storage_begin(); }

  T* inline_storage_begin() { return reinterpret_cast<T*>(&inline_storage_); }
  const T* inline_storage_begin() const {
    return reinterpret_cast<const T*>(&inline_storage_);
  }
};

}  // namespace base
}  // namespace v8

#endif  // V8_BASE_SMALL_VECTOR_H_