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