1
0

throwing_allocator.hpp 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP
  2. #define ENTT_COMMON_THROWING_ALLOCATOR_HPP
  3. #include <concepts>
  4. #include <cstddef>
  5. #include <limits>
  6. #include <memory>
  7. #include <type_traits>
  8. #include <entt/container/dense_map.hpp>
  9. #include <entt/core/fwd.hpp>
  10. #include <entt/core/type_info.hpp>
  11. namespace test {
  12. struct throwing_allocator_exception {};
  13. template<typename Type>
  14. class throwing_allocator {
  15. template<typename Other>
  16. friend class throwing_allocator;
  17. template<typename Other>
  18. requires (!std::same_as<Type, void> || std::constructible_from<std::allocator<Type>, std::allocator<Other>>)
  19. throwing_allocator(int, const throwing_allocator<Other> &other)
  20. : allocator{other.allocator},
  21. config{other.config} {}
  22. template<typename Other>
  23. requires std::same_as<Type, void> || std::constructible_from<std::allocator<Type>, std::allocator<Other>>
  24. throwing_allocator(char, const throwing_allocator<Other> &other)
  25. : allocator{},
  26. config{other.config} {}
  27. public:
  28. using value_type = Type;
  29. using pointer = value_type *;
  30. using const_pointer = const value_type *;
  31. using void_pointer = void *;
  32. using const_void_pointer = const void *;
  33. using propagate_on_container_move_assignment = std::true_type;
  34. using propagate_on_container_swap = std::true_type;
  35. using container_type = entt::dense_map<entt::id_type, std::size_t>;
  36. template<typename Other>
  37. struct rebind {
  38. using other = throwing_allocator<Other>;
  39. };
  40. throwing_allocator()
  41. : allocator{},
  42. config{std::allocate_shared<container_type>(allocator)} {}
  43. template<typename Other>
  44. throwing_allocator(const throwing_allocator<Other> &other)
  45. // std::allocator<void> has no cross constructors (waiting for C++20)
  46. : throwing_allocator{0, other} {}
  47. pointer allocate(std::size_t length) {
  48. if(const auto hash = entt::type_id<Type>().hash(); config->contains(hash)) {
  49. if(auto &elem = (*config)[hash]; elem == 0u) {
  50. config->erase(hash);
  51. throw throwing_allocator_exception{};
  52. } else {
  53. --elem;
  54. }
  55. }
  56. return allocator.allocate(length);
  57. }
  58. void deallocate(pointer mem, std::size_t length) {
  59. allocator.deallocate(mem, length);
  60. }
  61. template<typename Other>
  62. void throw_counter(const std::size_t len) {
  63. (*config)[entt::type_id<Other>().hash()] = len;
  64. }
  65. [[nodiscard]] bool operator==(const throwing_allocator<Type> &) const noexcept {
  66. return true;
  67. }
  68. private:
  69. std::allocator<Type> allocator;
  70. std::shared_ptr<container_type> config;
  71. };
  72. } // namespace test
  73. #endif