sbo_resource.hpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //
  2. // Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_COBALT_DETAIL_SBO_BUFFER_RESOURCE_HPP
  8. #define BOOST_COBALT_DETAIL_SBO_BUFFER_RESOURCE_HPP
  9. #include <boost/cobalt/config.hpp>
  10. namespace boost::cobalt::detail
  11. {
  12. struct sbo_resource
  13. #if !defined(BOOST_COBALT_NO_PMR)
  14. final : pmr::memory_resource
  15. #endif
  16. {
  17. private:
  18. struct block_
  19. {
  20. void* p{nullptr};
  21. std::size_t avail{0u};
  22. std::size_t size{0u};
  23. bool fragmented{false};
  24. };
  25. block_ buffer_;
  26. #if !defined(BOOST_COBALT_NO_PMR)
  27. pmr::memory_resource * upstream_;
  28. #endif
  29. constexpr std::size_t align_as_max_(std::size_t size)
  30. {
  31. auto diff = size % alignof(std::max_align_t );
  32. if (diff > 0)
  33. return size + alignof(std::max_align_t) - diff;
  34. else
  35. return size;
  36. }
  37. constexpr void align_as_max_()
  38. {
  39. const auto buffer = static_cast<char*>(buffer_.p) - static_cast<char*>(nullptr);
  40. const auto diff = buffer % alignof(std::max_align_t );
  41. if (diff > 0)
  42. {
  43. const auto padding = alignof(std::max_align_t) - diff;
  44. buffer_.p = static_cast<void*>(static_cast<char*>(nullptr) + buffer + padding);
  45. if (padding >= buffer_.size) [[unlikely]]
  46. {
  47. buffer_.size = 0;
  48. buffer_.avail = 0;
  49. }
  50. else
  51. {
  52. buffer_.size -= padding;
  53. buffer_.avail -= padding;
  54. }
  55. }
  56. }
  57. public:
  58. constexpr sbo_resource(void * buffer, std::size_t size
  59. #if !defined(BOOST_COBALT_NO_PMR)
  60. , pmr::memory_resource * upstream = pmr::get_default_resource()
  61. #endif
  62. ) : buffer_{buffer, size, size, false}
  63. #if !defined(BOOST_COBALT_NO_PMR)
  64. , upstream_(upstream)
  65. #endif
  66. {
  67. align_as_max_();
  68. }
  69. #if defined(BOOST_COBALT_NO_PMR)
  70. constexpr sbo_resource() : buffer_{nullptr, 0u, 0u, false} {}
  71. #else
  72. constexpr sbo_resource(pmr::memory_resource * upstream = pmr::get_default_resource())
  73. : buffer_{nullptr, 0u, 0u, false}, upstream_(upstream) {}
  74. #endif
  75. ~sbo_resource() = default;
  76. constexpr void * do_allocate(std::size_t size, std::size_t align)
  77. #if !defined(BOOST_COBALT_NO_PMR)
  78. override
  79. #endif
  80. {
  81. const auto sz = align_as_max_(size);
  82. if (sz <= buffer_.avail && !buffer_.fragmented) [[likely]]
  83. {
  84. auto p = static_cast<char*>(buffer_.p) + buffer_.size - buffer_.avail;
  85. buffer_.avail -= sz;
  86. return p;
  87. }
  88. else
  89. #if !defined(BOOST_COBALT_NO_PMR)
  90. return upstream_->allocate(size, align);
  91. #else
  92. return operator new(size, std::align_val_t(align));
  93. #endif
  94. }
  95. constexpr void do_deallocate(void * p, std::size_t size, std::size_t align)
  96. #if !defined(BOOST_COBALT_NO_PMR)
  97. override
  98. #endif
  99. {
  100. auto begin = static_cast<char*>(static_cast<char*>(buffer_.p));
  101. auto end = begin + buffer_.size;
  102. auto itr = static_cast<char*>(p);
  103. if(begin <= itr && itr < end) [[likely]]
  104. {
  105. const auto sz = align_as_max_(size);
  106. const auto used_mem_end = end - buffer_.avail;
  107. const auto dealloc_end = itr + sz;
  108. if (used_mem_end != dealloc_end )
  109. buffer_.fragmented = true;
  110. buffer_.avail += sz;
  111. if (buffer_.avail == buffer_.size)
  112. buffer_.fragmented = false;
  113. }
  114. else
  115. {
  116. #if !defined(BOOST_COBALT_NO_PMR)
  117. upstream_->deallocate(p, size, align);
  118. #else
  119. #if defined(__cpp_sized_deallocation)
  120. operator delete(p, size, std::align_val_t(align));
  121. #else
  122. operator delete(p, std::align_val_t(align));
  123. #endif
  124. #endif
  125. }
  126. }
  127. #if !defined(BOOST_COBALT_NO_PMR)
  128. constexpr bool do_is_equal(memory_resource const& other) const noexcept override
  129. {
  130. return this == &other;
  131. }
  132. #endif
  133. };
  134. inline sbo_resource * get_null_sbo_resource()
  135. {
  136. static sbo_resource empty_resource;
  137. return &empty_resource;
  138. }
  139. template<typename T>
  140. struct sbo_allocator
  141. {
  142. template<typename U>
  143. sbo_allocator(sbo_allocator<U> alloc) : resource_(alloc.resource_)
  144. {
  145. }
  146. using value_type = T;
  147. using size_type = std::size_t;
  148. using difference_type = std::ptrdiff_t;
  149. using propagate_on_container_move_assignment = std::true_type;
  150. [[nodiscard]] constexpr T* allocate( std::size_t n )
  151. {
  152. BOOST_ASSERT(resource_);
  153. return static_cast<T*>(resource_->do_allocate(sizeof(T) * n, alignof(T)));
  154. }
  155. constexpr void deallocate( T* p, std::size_t n )
  156. {
  157. BOOST_ASSERT(resource_);
  158. resource_->do_deallocate(p, sizeof(T) * n, alignof(T));
  159. }
  160. sbo_allocator(sbo_resource * resource) : resource_(resource) {}
  161. private:
  162. template<typename>
  163. friend struct sbo_allocator;
  164. sbo_resource * resource_{nullptr};
  165. };
  166. }
  167. #endif //BOOST_COBALT_DETAIL_SBO_BUFFER_RESOURCE_HPP