🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
12 KiB

  1. #ifndef ENTT_CORE_MEMORY_HPP
  2. #define ENTT_CORE_MEMORY_HPP
  3. #include <cstddef>
  4. #include <limits>
  5. #include <memory>
  6. #include <tuple>
  7. #include <type_traits>
  8. #include <utility>
  9. #include "../config/config.h"
  10. namespace entt {
  11. /**
  12. * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
  13. * @tparam Type Pointer type.
  14. * @param ptr Fancy or raw pointer.
  15. * @return A raw pointer that represents the address of the original pointer.
  16. */
  17. template<typename Type>
  18. [[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
  19. if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
  20. return ptr;
  21. } else {
  22. return to_address(std::forward<Type>(ptr).operator->());
  23. }
  24. }
  25. /**
  26. * @brief Utility function to design allocation-aware containers.
  27. * @tparam Allocator Type of allocator.
  28. * @param lhs A valid allocator.
  29. * @param rhs Another valid allocator.
  30. */
  31. template<typename Allocator>
  32. constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
  33. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
  34. lhs = rhs;
  35. }
  36. }
  37. /**
  38. * @brief Utility function to design allocation-aware containers.
  39. * @tparam Allocator Type of allocator.
  40. * @param lhs A valid allocator.
  41. * @param rhs Another valid allocator.
  42. */
  43. template<typename Allocator>
  44. constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
  45. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
  46. lhs = std::move(rhs);
  47. }
  48. }
  49. /**
  50. * @brief Utility function to design allocation-aware containers.
  51. * @tparam Allocator Type of allocator.
  52. * @param lhs A valid allocator.
  53. * @param rhs Another valid allocator.
  54. */
  55. template<typename Allocator>
  56. constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
  57. ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
  58. if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
  59. using std::swap;
  60. swap(lhs, rhs);
  61. }
  62. }
  63. /**
  64. * @brief Checks whether a value is a power of two or not.
  65. * @param value A value that may or may not be a power of two.
  66. * @return True if the value is a power of two, false otherwise.
  67. */
  68. [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
  69. return value && ((value & (value - 1)) == 0);
  70. }
  71. /**
  72. * @brief Computes the smallest power of two greater than or equal to a value.
  73. * @param value The value to use.
  74. * @return The smallest power of two greater than or equal to the given value.
  75. */
  76. [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
  77. ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
  78. std::size_t curr = value - (value != 0u);
  79. for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
  80. curr |= curr >> next;
  81. }
  82. return ++curr;
  83. }
  84. /**
  85. * @brief Fast module utility function (powers of two only).
  86. * @param value A value for which to calculate the modulus.
  87. * @param mod _Modulus_, it must be a power of two.
  88. * @return The common remainder.
  89. */
  90. [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
  91. ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
  92. return value & (mod - 1u);
  93. }
  94. /**
  95. * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
  96. * @tparam Args Types of arguments to use to construct the object.
  97. */
  98. template<typename Allocator>
  99. struct allocation_deleter: private Allocator {
  100. /*! @brief Allocator type. */
  101. using allocator_type = Allocator;
  102. /*! @brief Pointer type. */
  103. using pointer = typename std::allocator_traits<Allocator>::pointer;
  104. /**
  105. * @brief Inherited constructors.
  106. * @param alloc The allocator to use.
  107. */
  108. allocation_deleter(const allocator_type &alloc)
  109. : Allocator{alloc} {}
  110. /**
  111. * @brief Destroys the pointed object and deallocates its memory.
  112. * @param ptr A valid pointer to an object of the given type.
  113. */
  114. void operator()(pointer ptr) {
  115. using alloc_traits = typename std::allocator_traits<Allocator>;
  116. alloc_traits::destroy(*this, to_address(ptr));
  117. alloc_traits::deallocate(*this, ptr, 1u);
  118. }
  119. };
  120. /**
  121. * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
  122. * @tparam Type Type of object to allocate for and to construct.
  123. * @tparam Allocator Type of allocator used to manage memory and elements.
  124. * @tparam Args Types of arguments to use to construct the object.
  125. * @param allocator The allocator to use.
  126. * @param args Parameters to use to construct the object.
  127. * @return A properly initialized unique pointer with a custom deleter.
  128. */
  129. template<typename Type, typename Allocator, typename... Args>
  130. auto allocate_unique(Allocator &allocator, Args &&...args) {
  131. static_assert(!std::is_array_v<Type>, "Array types are not supported");
  132. using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
  133. using allocator_type = typename alloc_traits::allocator_type;
  134. allocator_type alloc{allocator};
  135. auto ptr = alloc_traits::allocate(alloc, 1u);
  136. ENTT_TRY {
  137. alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
  138. }
  139. ENTT_CATCH {
  140. alloc_traits::deallocate(alloc, ptr, 1u);
  141. ENTT_THROW;
  142. }
  143. return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
  144. }
  145. /**
  146. * @cond TURN_OFF_DOXYGEN
  147. * Internal details not to be documented.
  148. */
  149. namespace internal {
  150. template<typename Type>
  151. struct uses_allocator_construction {
  152. template<typename Allocator, typename... Params>
  153. static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
  154. if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
  155. return std::forward_as_tuple(std::forward<Params>(params)...);
  156. } else {
  157. static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
  158. if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
  159. return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
  160. } else {
  161. static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
  162. return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
  163. }
  164. }
  165. }
  166. };
  167. template<typename Type, typename Other>
  168. struct uses_allocator_construction<std::pair<Type, Other>> {
  169. using type = std::pair<Type, Other>;
  170. template<typename Allocator, typename First, typename Second>
  171. static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
  172. return std::make_tuple(
  173. std::piecewise_construct,
  174. std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
  175. std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
  176. }
  177. template<typename Allocator>
  178. static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
  179. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
  180. }
  181. template<typename Allocator, typename First, typename Second>
  182. static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
  183. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
  184. }
  185. template<typename Allocator, typename First, typename Second>
  186. static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
  187. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
  188. }
  189. template<typename Allocator, typename First, typename Second>
  190. static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
  191. return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
  192. }
  193. };
  194. } // namespace internal
  195. /**
  196. * Internal details not to be documented.
  197. * @endcond
  198. */
  199. /**
  200. * @brief Uses-allocator construction utility (waiting for C++20).
  201. *
  202. * Primarily intended for internal use. Prepares the argument list needed to
  203. * create an object of a given type by means of uses-allocator construction.
  204. *
  205. * @tparam Type Type to return arguments for.
  206. * @tparam Allocator Type of allocator used to manage memory and elements.
  207. * @tparam Args Types of arguments to use to construct the object.
  208. * @param allocator The allocator to use.
  209. * @param args Parameters to use to construct the object.
  210. * @return The arguments needed to create an object of the given type.
  211. */
  212. template<typename Type, typename Allocator, typename... Args>
  213. constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
  214. return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
  215. }
  216. /**
  217. * @brief Uses-allocator construction utility (waiting for C++20).
  218. *
  219. * Primarily intended for internal use. Creates an object of a given type by
  220. * means of uses-allocator construction.
  221. *
  222. * @tparam Type Type of object to create.
  223. * @tparam Allocator Type of allocator used to manage memory and elements.
  224. * @tparam Args Types of arguments to use to construct the object.
  225. * @param allocator The allocator to use.
  226. * @param args Parameters to use to construct the object.
  227. * @return A newly created object of the given type.
  228. */
  229. template<typename Type, typename Allocator, typename... Args>
  230. constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
  231. return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
  232. }
  233. /**
  234. * @brief Uses-allocator construction utility (waiting for C++20).
  235. *
  236. * Primarily intended for internal use. Creates an object of a given type by
  237. * means of uses-allocator construction at an uninitialized memory location.
  238. *
  239. * @tparam Type Type of object to create.
  240. * @tparam Allocator Type of allocator used to manage memory and elements.
  241. * @tparam Args Types of arguments to use to construct the object.
  242. * @param value Memory location in which to place the object.
  243. * @param allocator The allocator to use.
  244. * @param args Parameters to use to construct the object.
  245. * @return A pointer to the newly created object of the given type.
  246. */
  247. template<typename Type, typename Allocator, typename... Args>
  248. constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
  249. return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
  250. }
  251. } // namespace entt
  252. #endif