cache.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. #ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP
  2. #define ENTT_RESOURCE_RESOURCE_CACHE_HPP
  3. #include <cstddef>
  4. #include <functional>
  5. #include <iterator>
  6. #include <memory>
  7. #include <tuple>
  8. #include <type_traits>
  9. #include <utility>
  10. #include "../config/config.h"
  11. #include "../container/dense_map.hpp"
  12. #include "../core/compressed_pair.hpp"
  13. #include "../core/fwd.hpp"
  14. #include "../core/iterator.hpp"
  15. #include "../core/utility.hpp"
  16. #include "fwd.hpp"
  17. #include "loader.hpp"
  18. #include "resource.hpp"
  19. namespace entt {
  20. /**
  21. * @cond TURN_OFF_DOXYGEN
  22. * Internal details not to be documented.
  23. */
  24. namespace internal {
  25. template<typename Type, typename It>
  26. class resource_cache_iterator final {
  27. template<typename, typename>
  28. friend class resource_cache_iterator;
  29. public:
  30. using value_type = std::pair<id_type, resource<Type>>;
  31. using pointer = input_iterator_pointer<value_type>;
  32. using reference = value_type;
  33. using difference_type = std::ptrdiff_t;
  34. using iterator_category = std::input_iterator_tag;
  35. resource_cache_iterator() ENTT_NOEXCEPT = default;
  36. resource_cache_iterator(const It iter) ENTT_NOEXCEPT
  37. : it{iter} {}
  38. template<typename Other, typename = std::enable_if_t<std::is_const_v<Type>>>
  39. resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) ENTT_NOEXCEPT
  40. : it{other.it} {}
  41. resource_cache_iterator &operator++() ENTT_NOEXCEPT {
  42. return ++it, *this;
  43. }
  44. resource_cache_iterator operator++(int) ENTT_NOEXCEPT {
  45. resource_cache_iterator orig = *this;
  46. return ++(*this), orig;
  47. }
  48. resource_cache_iterator &operator--() ENTT_NOEXCEPT {
  49. return --it, *this;
  50. }
  51. resource_cache_iterator operator--(int) ENTT_NOEXCEPT {
  52. resource_cache_iterator orig = *this;
  53. return operator--(), orig;
  54. }
  55. resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
  56. it += value;
  57. return *this;
  58. }
  59. resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
  60. resource_cache_iterator copy = *this;
  61. return (copy += value);
  62. }
  63. resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
  64. return (*this += -value);
  65. }
  66. resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
  67. return (*this + -value);
  68. }
  69. [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
  70. return {it[value].first, resource<Type>{it[value].second}};
  71. }
  72. [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
  73. return (*this)[0];
  74. }
  75. [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
  76. return operator*();
  77. }
  78. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  79. friend std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
  80. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  81. friend bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
  82. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  83. friend bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT;
  84. private:
  85. It it;
  86. };
  87. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  88. [[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  89. return lhs.it - rhs.it;
  90. }
  91. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  92. [[nodiscard]] bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  93. return lhs.it == rhs.it;
  94. }
  95. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  96. [[nodiscard]] bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  97. return !(lhs == rhs);
  98. }
  99. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  100. [[nodiscard]] bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  101. return lhs.it < rhs.it;
  102. }
  103. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  104. [[nodiscard]] bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  105. return rhs < lhs;
  106. }
  107. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  108. [[nodiscard]] bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  109. return !(lhs > rhs);
  110. }
  111. template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
  112. [[nodiscard]] bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT {
  113. return !(lhs < rhs);
  114. }
  115. } // namespace internal
  116. /**
  117. * Internal details not to be documented.
  118. * @endcond
  119. */
  120. /**
  121. * @brief Basic cache for resources of any type.
  122. * @tparam Type Type of resources managed by a cache.
  123. * @tparam Loader Type of loader used to create the resources.
  124. * @tparam Allocator Type of allocator used to manage memory and elements.
  125. */
  126. template<typename Type, typename Loader, typename Allocator>
  127. class resource_cache {
  128. using alloc_traits = typename std::allocator_traits<Allocator>;
  129. static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
  130. using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
  131. using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
  132. public:
  133. /*! @brief Resource type. */
  134. using value_type = Type;
  135. /*! @brief Unsigned integer type. */
  136. using size_type = std::size_t;
  137. /*! @brief Loader type. */
  138. using loader_type = Loader;
  139. /*! @brief Allocator type. */
  140. using allocator_type = Allocator;
  141. /*! @brief Input iterator type. */
  142. using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>;
  143. /*! @brief Constant input iterator type. */
  144. using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>;
  145. /*! @brief Default constructor. */
  146. resource_cache()
  147. : resource_cache{loader_type{}} {}
  148. /**
  149. * @brief Constructs an empty cache with a given allocator.
  150. * @param allocator The allocator to use.
  151. */
  152. explicit resource_cache(const allocator_type &allocator)
  153. : resource_cache{loader_type{}, allocator} {}
  154. /**
  155. * @brief Constructs an empty cache with a given allocator and loader.
  156. * @param callable The loader to use.
  157. * @param allocator The allocator to use.
  158. */
  159. explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{})
  160. : pool{container_type{allocator}, callable} {}
  161. /*! @brief Default copy constructor. */
  162. resource_cache(const resource_cache &) = default;
  163. /**
  164. * @brief Allocator-extended copy constructor.
  165. * @param other The instance to copy from.
  166. * @param allocator The allocator to use.
  167. */
  168. resource_cache(const resource_cache &other, const allocator_type &allocator)
  169. : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {}
  170. /*! @brief Default move constructor. */
  171. resource_cache(resource_cache &&) = default;
  172. /**
  173. * @brief Allocator-extended move constructor.
  174. * @param other The instance to move from.
  175. * @param allocator The allocator to use.
  176. */
  177. resource_cache(resource_cache &&other, const allocator_type &allocator)
  178. : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {}
  179. /**
  180. * @brief Default copy assignment operator.
  181. * @return This cache.
  182. */
  183. resource_cache &operator=(const resource_cache &) = default;
  184. /**
  185. * @brief Default move assignment operator.
  186. * @return This cache.
  187. */
  188. resource_cache &operator=(resource_cache &&) = default;
  189. /**
  190. * @brief Returns the associated allocator.
  191. * @return The associated allocator.
  192. */
  193. [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
  194. return pool.first().get_allocator();
  195. }
  196. /**
  197. * @brief Returns an iterator to the beginning.
  198. *
  199. * The returned iterator points to the first instance of the cache. If the
  200. * cache is empty, the returned iterator will be equal to `end()`.
  201. *
  202. * @return An iterator to the first instance of the internal cache.
  203. */
  204. [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
  205. return pool.first().begin();
  206. }
  207. /*! @copydoc cbegin */
  208. [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
  209. return cbegin();
  210. }
  211. /*! @copydoc begin */
  212. [[nodiscard]] iterator begin() ENTT_NOEXCEPT {
  213. return pool.first().begin();
  214. }
  215. /**
  216. * @brief Returns an iterator to the end.
  217. *
  218. * The returned iterator points to the element following the last instance
  219. * of the cache. Attempting to dereference the returned iterator results in
  220. * undefined behavior.
  221. *
  222. * @return An iterator to the element following the last instance of the
  223. * internal cache.
  224. */
  225. [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
  226. return pool.first().end();
  227. }
  228. /*! @copydoc cend */
  229. [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
  230. return cend();
  231. }
  232. /*! @copydoc end */
  233. [[nodiscard]] iterator end() ENTT_NOEXCEPT {
  234. return pool.first().end();
  235. }
  236. /**
  237. * @brief Returns true if a cache contains no resources, false otherwise.
  238. * @return True if the cache contains no resources, false otherwise.
  239. */
  240. [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
  241. return pool.first().empty();
  242. }
  243. /**
  244. * @brief Number of resources managed by a cache.
  245. * @return Number of resources currently stored.
  246. */
  247. [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
  248. return pool.first().size();
  249. }
  250. /*! @brief Clears a cache. */
  251. void clear() ENTT_NOEXCEPT {
  252. pool.first().clear();
  253. }
  254. /**
  255. * @brief Loads a resource, if its identifier does not exist.
  256. *
  257. * Arguments are forwarded directly to the loader and _consumed_ only if the
  258. * resource doesn't already exist.
  259. *
  260. * @warning
  261. * If the resource isn't loaded correctly, the returned handle could be
  262. * invalid and any use of it will result in undefined behavior.
  263. *
  264. * @tparam Args Types of arguments to use to load the resource if required.
  265. * @param id Unique resource identifier.
  266. * @param args Arguments to use to load the resource if required.
  267. * @return A pair consisting of an iterator to the inserted element (or to
  268. * the element that prevented the insertion) and a bool denoting whether the
  269. * insertion took place.
  270. */
  271. template<typename... Args>
  272. std::pair<iterator, bool> load(const id_type id, Args &&...args) {
  273. if(auto it = pool.first().find(id); it != pool.first().end()) {
  274. return {it, false};
  275. }
  276. return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...));
  277. }
  278. /**
  279. * @brief Force loads a resource, if its identifier does not exist.
  280. * @copydetails load
  281. */
  282. template<typename... Args>
  283. std::pair<iterator, bool> force_load(const id_type id, Args &&...args) {
  284. return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true};
  285. }
  286. /**
  287. * @brief Returns a handle for a given resource identifier.
  288. *
  289. * @warning
  290. * There is no guarantee that the returned handle is valid.<br/>
  291. * If it is not, any use will result in indefinite behavior.
  292. *
  293. * @param id Unique resource identifier.
  294. * @return A handle for the given resource.
  295. */
  296. [[nodiscard]] resource<const value_type> operator[](const id_type id) const {
  297. if(auto it = pool.first().find(id); it != pool.first().cend()) {
  298. return resource<const value_type>{it->second};
  299. }
  300. return {};
  301. }
  302. /*! @copydoc operator[] */
  303. [[nodiscard]] resource<value_type> operator[](const id_type id) {
  304. if(auto it = pool.first().find(id); it != pool.first().end()) {
  305. return resource<value_type>{it->second};
  306. }
  307. return {};
  308. }
  309. /**
  310. * @brief Checks if a cache contains a given identifier.
  311. * @param id Unique resource identifier.
  312. * @return True if the cache contains the resource, false otherwise.
  313. */
  314. [[nodiscard]] bool contains(const id_type id) const {
  315. return pool.first().contains(id);
  316. }
  317. /**
  318. * @brief Removes an element from a given position.
  319. * @param pos An iterator to the element to remove.
  320. * @return An iterator following the removed element.
  321. */
  322. iterator erase(const_iterator pos) {
  323. const auto it = pool.first().begin();
  324. return pool.first().erase(it + (pos - const_iterator{it}));
  325. }
  326. /**
  327. * @brief Removes the given elements from a cache.
  328. * @param first An iterator to the first element of the range of elements.
  329. * @param last An iterator past the last element of the range of elements.
  330. * @return An iterator following the last removed element.
  331. */
  332. iterator erase(const_iterator first, const_iterator last) {
  333. const auto it = pool.first().begin();
  334. return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it}));
  335. }
  336. /**
  337. * @brief Removes the given elements from a cache.
  338. * @param id Unique resource identifier.
  339. * @return Number of resources erased (either 0 or 1).
  340. */
  341. size_type erase(const id_type id) {
  342. return pool.first().erase(id);
  343. }
  344. /**
  345. * @brief Returns the loader used to create resources.
  346. * @return The loader used to create resources.
  347. */
  348. [[nodiscard]] loader_type loader() const {
  349. return pool.second();
  350. }
  351. private:
  352. compressed_pair<container_type, loader_type> pool;
  353. };
  354. } // namespace entt
  355. #endif