monotonic_resource.hpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. //
  2. // based on boost.json
  3. //
  4. // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
  5. // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
  6. // Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP
  12. #define BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP
  13. #include <boost/cobalt/config.hpp>
  14. #include <new>
  15. namespace boost::cobalt::detail
  16. {
  17. struct monotonic_resource
  18. {
  19. private:
  20. struct block_
  21. {
  22. void* p;
  23. std::size_t avail;
  24. std::size_t size;
  25. std::align_val_t aligned;
  26. block_* next;
  27. };
  28. block_ buffer_;
  29. block_* head_ = &buffer_;
  30. std::size_t chunk_size_{buffer_.size};
  31. public:
  32. constexpr monotonic_resource(void * buffer, std::size_t size)
  33. : buffer_{buffer, size, size, std::align_val_t{0u}, nullptr} {}
  34. constexpr monotonic_resource(std::size_t chunk_size = 1024)
  35. : buffer_{nullptr, 0u, 0u, std::align_val_t{0u}, nullptr}, chunk_size_(chunk_size) {}
  36. monotonic_resource(monotonic_resource && lhs) noexcept = delete;
  37. constexpr ~monotonic_resource()
  38. {
  39. if (head_ != &buffer_)
  40. release();
  41. }
  42. constexpr void release()
  43. {
  44. head_ = &buffer_;
  45. auto nx = buffer_.next;
  46. head_->next = nullptr;
  47. head_->avail = head_->size;
  48. while (nx != nullptr)
  49. {
  50. auto p = nx;
  51. nx = nx->next;
  52. #if defined(__cpp_sized_deallocation)
  53. const auto size = sizeof(block_) + p->size;
  54. operator delete(p->p, size, p->aligned);
  55. #else
  56. operator delete(p->p, p->aligned);
  57. #endif
  58. }
  59. }
  60. constexpr void * allocate(std::size_t size, std::align_val_t align_ = std::align_val_t(alignof(std::max_align_t)))
  61. {
  62. const auto align = (std::max)(static_cast<std::size_t>(align_), alignof(block_));
  63. // let's say size = 11, and align is 8, that leaves us with 3
  64. {
  65. const auto align_offset = size % align ;
  66. // padding is 5
  67. const auto padding = align - align_offset;
  68. const auto needed_size = size + padding;
  69. if (needed_size <= head_->avail) // fits, but we need to check alignment too
  70. {
  71. const auto offset = head_->size - head_->avail;
  72. auto pp = static_cast<char*>(head_->p) + offset + padding;
  73. head_->avail -= needed_size;
  74. return pp; // done
  75. }
  76. }
  77. // alright, we need to alloc something.
  78. const auto mem_size = (std::max)(chunk_size_, size);
  79. // add padding at the end
  80. const auto offset = (mem_size % alignof(block_));
  81. const auto padding = offset == 0 ? 0u : (alignof(block_) - offset);
  82. // size to allocate
  83. const auto raw_size = mem_size + padding;
  84. const auto alloc_size = raw_size + sizeof(block_);
  85. const auto aligned = std::align_val_t(align);
  86. const auto mem = ::operator new(alloc_size, aligned);
  87. const auto block_location = static_cast<char*>(mem) + mem_size + offset;
  88. head_ = head_->next = new (block_location) block_{mem, raw_size - size, raw_size, aligned, nullptr};
  89. return mem;
  90. }
  91. };
  92. template<typename T>
  93. struct monotonic_allocator
  94. {
  95. template<typename U>
  96. monotonic_allocator(monotonic_allocator<U> alloc) : resource_(alloc.resource_)
  97. {
  98. }
  99. using value_type = T;
  100. using size_type = std::size_t;
  101. using difference_type = std::ptrdiff_t;
  102. using propagate_on_container_move_assignment = std::true_type;
  103. [[nodiscard]] constexpr T* allocate( std::size_t n )
  104. {
  105. if (resource_)
  106. return static_cast<T*>(
  107. resource_->allocate(
  108. sizeof(T) * n,
  109. std::align_val_t(alignof(T))));
  110. else
  111. return std::allocator<T>().allocate(n);
  112. }
  113. constexpr void deallocate( T* p, std::size_t n )
  114. {
  115. if (!resource_)
  116. std::allocator<T>().deallocate(p, n);
  117. }
  118. monotonic_allocator(monotonic_resource * resource = nullptr) : resource_(resource) {}
  119. private:
  120. template<typename>
  121. friend struct monotonic_allocator;
  122. monotonic_resource * resource_{nullptr};
  123. };
  124. }
  125. #endif //BOOST_COBALT_DETAIL_MONOTONIC_BUFFER_RESOURCE_HPP