bounded_inline_vector_impl.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * Copyright 2020 The WebRTC Project Authors. All rights reserved.
  3. *
  4. * Use of this source code is governed by a BSD-style license
  5. * that can be found in the LICENSE file in the root of the source
  6. * tree. An additional intellectual property rights grant can be found
  7. * in the file PATENTS. All contributing project authors may
  8. * be found in the AUTHORS file in the root of the source tree.
  9. */
  10. #ifndef RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
  11. #define RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_
  12. #include <stdint.h>
  13. #include <cstring>
  14. #include <memory>
  15. #include <type_traits>
  16. #include <utility>
  17. namespace webrtc {
  18. namespace bounded_inline_vector_impl {
  19. template <bool...>
  20. struct BoolPack;
  21. // Tests if all its parameters (x0, x1, ..., xn) are true. The implementation
  22. // checks whether (x0, x1, ..., xn, true) == (true, x0, x1, ..., xn), which is
  23. // true iff true == x0 && x0 == x1 && x1 == x2 ... && xn-1 == xn && xn == true.
  24. template <bool... Bs>
  25. using AllTrue = std::is_same<BoolPack<Bs..., true>, BoolPack<true, Bs...>>;
  26. template <typename To, typename... Froms>
  27. using AllConvertible = AllTrue<std::is_convertible<Froms, To>::value...>;
  28. // Initializes part of an uninitialized array. Unlike normal array
  29. // initialization, does not zero the remaining array elements. Caller is
  30. // responsible for ensuring that there is enough space in `data`.
  31. template <typename T>
  32. void InitializeElements(T* data) {}
  33. template <typename T, typename U, typename... Us>
  34. void InitializeElements(T* data, U&& element, Us&&... elements) {
  35. // Placement new, because we construct a new object in uninitialized memory.
  36. ::new (data) T(std::forward<U>(element));
  37. InitializeElements(data + 1, std::forward<Us>(elements)...);
  38. }
  39. // Default initializes uninitialized array elements.
  40. // TODO(kwiberg): Replace with std::uninitialized_default_construct_n() (C++17).
  41. template <typename T>
  42. void DefaultInitializeElements(T* data, int size) {
  43. for (int i = 0; i < size; ++i) {
  44. // Placement new, because we construct a new object in uninitialized memory.
  45. ::new (&data[i]) T;
  46. }
  47. }
  48. // Copies from source to uninitialized destination. Caller is responsible for
  49. // ensuring that there is enough space in `dst_data`.
  50. template <typename T>
  51. void CopyElements(const T* src_data, int src_size, T* dst_data, int* dst_size) {
  52. if /*constexpr*/ (std::is_trivially_copy_constructible<T>::value) {
  53. std::memcpy(dst_data, src_data, src_size * sizeof(T));
  54. } else {
  55. std::uninitialized_copy_n(src_data, src_size, dst_data);
  56. }
  57. *dst_size = src_size;
  58. }
  59. // Moves from source to uninitialized destination. Caller is responsible for
  60. // ensuring that there is enough space in `dst_data`.
  61. template <typename T>
  62. void MoveElements(T* src_data, int src_size, T* dst_data, int* dst_size) {
  63. if /*constexpr*/ (std::is_trivially_move_constructible<T>::value) {
  64. std::memcpy(dst_data, src_data, src_size * sizeof(T));
  65. } else {
  66. // TODO(kwiberg): Use std::uninitialized_move_n() instead (C++17).
  67. for (int i = 0; i < src_size; ++i) {
  68. // Placement new, because we create a new object in uninitialized
  69. // memory.
  70. ::new (&dst_data[i]) T(std::move(src_data[i]));
  71. }
  72. }
  73. *dst_size = src_size;
  74. }
  75. // Destroys elements, leaving them uninitialized.
  76. template <typename T>
  77. void DestroyElements(T* data, int size) {
  78. if /*constexpr*/ (!std::is_trivially_destructible<T>::value) {
  79. for (int i = 0; i < size; ++i) {
  80. data[i].~T();
  81. }
  82. }
  83. }
  84. // If elements are trivial and the total capacity is at most this many bytes,
  85. // copy everything instead of just the elements that are in use; this is more
  86. // efficient, and makes BoundedInlineVector trivially copyable.
  87. static constexpr int kSmallSize = 64;
  88. // Storage implementations.
  89. //
  90. // There are diferent Storage structs for diferent kinds of element types. The
  91. // common contract is the following:
  92. //
  93. // * They have public `size` variables and `data` array members.
  94. //
  95. // * Their owner is responsible for enforcing the invariant that the first
  96. // `size` elements in `data` are initialized, and the remaining elements are
  97. // not initialized.
  98. //
  99. // * They implement default construction, construction with one or more
  100. // elements, copy/move construction, copy/move assignment, and destruction;
  101. // the owner must ensure that the invariant holds whenever these operations
  102. // occur.
  103. // Storage implementation for nontrivial element types.
  104. template <typename T,
  105. int fixed_capacity,
  106. bool is_trivial = std::is_trivial<T>::value,
  107. bool is_small = (sizeof(T) * fixed_capacity <= kSmallSize)>
  108. struct Storage {
  109. static_assert(!std::is_trivial<T>::value, "");
  110. template <
  111. typename... Ts,
  112. typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
  113. explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
  114. InitializeElements(data, std::forward<Ts>(elements)...);
  115. }
  116. Storage(const Storage& other) {
  117. CopyElements(other.data, other.size, data, &size);
  118. }
  119. Storage(Storage&& other) {
  120. MoveElements(other.data, other.size, data, &size);
  121. }
  122. Storage& operator=(const Storage& other) {
  123. if (this != &other) {
  124. DestroyElements(data, size);
  125. CopyElements(other.data, other.size, data, &size);
  126. }
  127. return *this;
  128. }
  129. Storage& operator=(Storage&& other) {
  130. DestroyElements(data, size);
  131. size = 0; // Needed in case of self assignment.
  132. MoveElements(other.data, other.size, data, &size);
  133. return *this;
  134. }
  135. ~Storage() { DestroyElements(data, size); }
  136. int size;
  137. union {
  138. // Since this array is in a union, we get to construct and destroy it
  139. // manually.
  140. T data[fixed_capacity]; // NOLINT(runtime/arrays)
  141. };
  142. };
  143. // Storage implementation for trivial element types when the capacity is small
  144. // enough that we can cheaply copy everything.
  145. template <typename T, int fixed_capacity>
  146. struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/true> {
  147. static_assert(std::is_trivial<T>::value, "");
  148. static_assert(sizeof(T) * fixed_capacity <= kSmallSize, "");
  149. template <
  150. typename... Ts,
  151. typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
  152. explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
  153. InitializeElements(data, std::forward<Ts>(elements)...);
  154. }
  155. Storage(const Storage&) = default;
  156. Storage& operator=(const Storage&) = default;
  157. ~Storage() = default;
  158. int size;
  159. T data[fixed_capacity]; // NOLINT(runtime/arrays)
  160. };
  161. // Storage implementation for trivial element types when the capacity is large
  162. // enough that we want to avoid copying uninitialized elements.
  163. template <typename T, int fixed_capacity>
  164. struct Storage<T, fixed_capacity, /*is_trivial=*/true, /*is_small=*/false> {
  165. static_assert(std::is_trivial<T>::value, "");
  166. static_assert(sizeof(T) * fixed_capacity > kSmallSize, "");
  167. template <
  168. typename... Ts,
  169. typename std::enable_if_t<AllConvertible<T, Ts...>::value>* = nullptr>
  170. explicit Storage(Ts&&... elements) : size(sizeof...(Ts)) {
  171. InitializeElements(data, std::forward<Ts>(elements)...);
  172. }
  173. Storage(const Storage& other) : size(other.size) {
  174. std::memcpy(data, other.data, other.size * sizeof(T));
  175. }
  176. Storage& operator=(const Storage& other) {
  177. if (this != &other) {
  178. size = other.size;
  179. std::memcpy(data, other.data, other.size * sizeof(T));
  180. }
  181. return *this;
  182. }
  183. ~Storage() = default;
  184. int size;
  185. union {
  186. T data[fixed_capacity]; // NOLINT(runtime/arrays)
  187. };
  188. };
  189. } // namespace bounded_inline_vector_impl
  190. } // namespace webrtc
  191. #endif // RTC_BASE_BOUNDED_INLINE_VECTOR_IMPL_H_