vector_buffer.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright 2017 The Chromium 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. #ifndef BASE_CONTAINERS_VECTOR_BUFFER_H_
  5. #define BASE_CONTAINERS_VECTOR_BUFFER_H_
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <type_traits>
  9. #include <utility>
  10. #include "base/check_op.h"
  11. #include "base/containers/util.h"
  12. #include "base/numerics/checked_math.h"
  13. namespace base {
  14. namespace internal {
  15. // Internal implementation detail of base/containers.
  16. //
  17. // Implements a vector-like buffer that holds a certain capacity of T. Unlike
  18. // std::vector, VectorBuffer never constructs or destructs its arguments, and
  19. // can't change sizes. But it does implement templates to assist in efficient
  20. // moving and destruction of those items manually.
  21. //
  22. // In particular, the destructor function does not iterate over the items if
  23. // there is no destructor. Moves should be implemented as a memcpy/memmove for
  24. // trivially copyable objects (POD) otherwise, it should be a std::move if
  25. // possible, and as a last resort it falls back to a copy. This behavior is
  26. // similar to std::vector.
  27. //
  28. // No special consideration is done for noexcept move constructors since
  29. // we compile without exceptions.
  30. //
  31. // The current API does not support moving overlapping ranges.
  32. template <typename T>
  33. class VectorBuffer {
  34. public:
  35. constexpr VectorBuffer() = default;
  36. #if defined(__clang__) && !defined(__native_client__)
  37. // This constructor converts an uninitialized void* to a T* which triggers
  38. // clang Control Flow Integrity. Since this is as-designed, disable.
  39. __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
  40. #endif
  41. VectorBuffer(size_t count)
  42. : buffer_(reinterpret_cast<T*>(
  43. malloc(CheckMul(sizeof(T), count).ValueOrDie()))),
  44. capacity_(count) {
  45. }
  46. VectorBuffer(VectorBuffer&& other) noexcept
  47. : buffer_(other.buffer_), capacity_(other.capacity_) {
  48. other.buffer_ = nullptr;
  49. other.capacity_ = 0;
  50. }
  51. VectorBuffer(const VectorBuffer&) = delete;
  52. VectorBuffer& operator=(const VectorBuffer&) = delete;
  53. ~VectorBuffer() { free(buffer_); }
  54. VectorBuffer& operator=(VectorBuffer&& other) {
  55. free(buffer_);
  56. buffer_ = other.buffer_;
  57. capacity_ = other.capacity_;
  58. other.buffer_ = nullptr;
  59. other.capacity_ = 0;
  60. return *this;
  61. }
  62. size_t capacity() const { return capacity_; }
  63. T& operator[](size_t i) {
  64. // TODO(crbug.com/817982): Some call sites (at least circular_deque.h) are
  65. // calling this with `i == capacity_` as a way of getting `end()`. Therefore
  66. // we have to allow this for now (`i <= capacity_`), until we fix those call
  67. // sites to use real iterators. This comment applies here and to `const T&
  68. // operator[]`, below.
  69. CHECK_LE(i, capacity_);
  70. return buffer_[i];
  71. }
  72. const T& operator[](size_t i) const {
  73. CHECK_LE(i, capacity_);
  74. return buffer_[i];
  75. }
  76. T* begin() { return buffer_; }
  77. T* end() { return &buffer_[capacity_]; }
  78. // DestructRange ------------------------------------------------------------
  79. // Trivially destructible objects need not have their destructors called.
  80. template <typename T2 = T,
  81. typename std::enable_if<std::is_trivially_destructible<T2>::value,
  82. int>::type = 0>
  83. void DestructRange(T* begin, T* end) {}
  84. // Non-trivially destructible objects must have their destructors called
  85. // individually.
  86. template <typename T2 = T,
  87. typename std::enable_if<!std::is_trivially_destructible<T2>::value,
  88. int>::type = 0>
  89. void DestructRange(T* begin, T* end) {
  90. CHECK_LE(begin, end);
  91. while (begin != end) {
  92. begin->~T();
  93. begin++;
  94. }
  95. }
  96. // MoveRange ----------------------------------------------------------------
  97. //
  98. // The destructor will be called (as necessary) for all moved types. The
  99. // ranges must not overlap.
  100. //
  101. // The parameters and begin and end (one past the last) of the input buffer,
  102. // and the address of the first element to copy to. There must be sufficient
  103. // room in the destination for all items in the range [begin, end).
  104. // Trivially copyable types can use memcpy. trivially copyable implies
  105. // that there is a trivial destructor as we don't have to call it.
  106. template <typename T2 = T,
  107. typename std::enable_if<base::is_trivially_copyable<T2>::value,
  108. int>::type = 0>
  109. static void MoveRange(T* from_begin, T* from_end, T* to) {
  110. CHECK(!RangesOverlap(from_begin, from_end, to));
  111. memcpy(
  112. to, from_begin,
  113. CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie());
  114. }
  115. // Not trivially copyable, but movable: call the move constructor and
  116. // destruct the original.
  117. template <typename T2 = T,
  118. typename std::enable_if<std::is_move_constructible<T2>::value &&
  119. !base::is_trivially_copyable<T2>::value,
  120. int>::type = 0>
  121. static void MoveRange(T* from_begin, T* from_end, T* to) {
  122. CHECK(!RangesOverlap(from_begin, from_end, to));
  123. while (from_begin != from_end) {
  124. new (to) T(std::move(*from_begin));
  125. from_begin->~T();
  126. from_begin++;
  127. to++;
  128. }
  129. }
  130. // Not movable, not trivially copyable: call the copy constructor and
  131. // destruct the original.
  132. template <typename T2 = T,
  133. typename std::enable_if<!std::is_move_constructible<T2>::value &&
  134. !base::is_trivially_copyable<T2>::value,
  135. int>::type = 0>
  136. static void MoveRange(T* from_begin, T* from_end, T* to) {
  137. CHECK(!RangesOverlap(from_begin, from_end, to));
  138. while (from_begin != from_end) {
  139. new (to) T(*from_begin);
  140. from_begin->~T();
  141. from_begin++;
  142. to++;
  143. }
  144. }
  145. private:
  146. static bool RangesOverlap(const T* from_begin,
  147. const T* from_end,
  148. const T* to) {
  149. const auto from_begin_uintptr = get_uintptr(from_begin);
  150. const auto from_end_uintptr = get_uintptr(from_end);
  151. const auto to_uintptr = get_uintptr(to);
  152. return !(
  153. to >= from_end ||
  154. CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr))
  155. .ValueOrDie() <= from_begin_uintptr);
  156. }
  157. T* buffer_ = nullptr;
  158. size_t capacity_ = 0;
  159. };
  160. } // namespace internal
  161. } // namespace base
  162. #endif // BASE_CONTAINERS_VECTOR_BUFFER_H_