File indexing completed on 2025-12-16 09:44:25
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_COBALT_DETAIL_SBO_BUFFER_RESOURCE_HPP
0009 #define BOOST_COBALT_DETAIL_SBO_BUFFER_RESOURCE_HPP
0010
0011 #include <boost/cobalt/config.hpp>
0012
0013 namespace boost::cobalt::detail
0014 {
0015
0016 struct sbo_resource
0017 #if !defined(BOOST_COBALT_NO_PMR)
0018 final : pmr::memory_resource
0019 #endif
0020 {
0021 private:
0022 struct block_
0023 {
0024 void* p{nullptr};
0025 std::size_t avail{0u};
0026 std::size_t size{0u};
0027 bool fragmented{false};
0028 };
0029
0030 block_ buffer_;
0031 #if !defined(BOOST_COBALT_NO_PMR)
0032 pmr::memory_resource * upstream_;
0033 #endif
0034 constexpr std::size_t align_as_max_(std::size_t size)
0035 {
0036 auto diff = size % alignof(std::max_align_t );
0037 if (diff > 0)
0038 return size + alignof(std::max_align_t) - diff;
0039 else
0040 return size;
0041 }
0042 constexpr void align_as_max_()
0043 {
0044 const auto buffer = static_cast<char*>(buffer_.p) - static_cast<char*>(nullptr);
0045 const auto diff = buffer % alignof(std::max_align_t );
0046 if (diff > 0)
0047 {
0048 const auto padding = alignof(std::max_align_t) - diff;
0049 buffer_.p = static_cast<void*>(static_cast<char*>(nullptr) + buffer + padding);
0050 if (padding >= buffer_.size) [[unlikely]]
0051 {
0052 buffer_.size = 0;
0053 buffer_.avail = 0;
0054 }
0055 else
0056 {
0057 buffer_.size -= padding;
0058 buffer_.avail -= padding;
0059 }
0060
0061 }
0062 }
0063
0064 public:
0065 constexpr sbo_resource(void * buffer, std::size_t size
0066 #if !defined(BOOST_COBALT_NO_PMR)
0067 , pmr::memory_resource * upstream = pmr::get_default_resource()
0068 #endif
0069 ) : buffer_{buffer, size, size, false}
0070 #if !defined(BOOST_COBALT_NO_PMR)
0071 , upstream_(upstream)
0072 #endif
0073 {
0074 align_as_max_();
0075 }
0076
0077 #if defined(BOOST_COBALT_NO_PMR)
0078 constexpr sbo_resource() : buffer_{nullptr, 0u, 0u, false} {}
0079
0080 #else
0081 constexpr sbo_resource(pmr::memory_resource * upstream = pmr::get_default_resource())
0082 : buffer_{nullptr, 0u, 0u, false}, upstream_(upstream) {}
0083 #endif
0084
0085 ~sbo_resource() = default;
0086
0087 constexpr void * do_allocate(std::size_t size, std::size_t align)
0088 #if !defined(BOOST_COBALT_NO_PMR)
0089 override
0090 #endif
0091 {
0092 const auto sz = align_as_max_(size);
0093 if (sz <= buffer_.avail && !buffer_.fragmented) [[likely]]
0094 {
0095 auto p = static_cast<char*>(buffer_.p) + buffer_.size - buffer_.avail;
0096 buffer_.avail -= sz;
0097 return p;
0098 }
0099 else
0100 #if !defined(BOOST_COBALT_NO_PMR)
0101 return upstream_->allocate(size, align);
0102 #else
0103 return operator new(size, std::align_val_t(align));
0104 #endif
0105
0106 }
0107
0108 constexpr void do_deallocate(void * p, std::size_t size, std::size_t align)
0109 #if !defined(BOOST_COBALT_NO_PMR)
0110 override
0111 #endif
0112 {
0113 auto begin = static_cast<char*>(static_cast<char*>(buffer_.p));
0114 auto end = begin + buffer_.size;
0115 auto itr = static_cast<char*>(p);
0116
0117 if(begin <= itr && itr < end) [[likely]]
0118 {
0119 const auto sz = align_as_max_(size);
0120 const auto used_mem_end = end - buffer_.avail;
0121 const auto dealloc_end = itr + sz;
0122 if (used_mem_end != dealloc_end )
0123 buffer_.fragmented = true;
0124 buffer_.avail += sz;
0125 if (buffer_.avail == buffer_.size)
0126 buffer_.fragmented = false;
0127 }
0128 else
0129 {
0130 #if !defined(BOOST_COBALT_NO_PMR)
0131 upstream_->deallocate(p, size, align);
0132 #else
0133 #if defined(__cpp_sized_deallocation)
0134 operator delete(p, size, std::align_val_t(align));
0135 #else
0136 operator delete(p, std::align_val_t(align));
0137 #endif
0138 #endif
0139
0140 }
0141 }
0142
0143 #if defined(BOOST_COBALT_NO_PMR)
0144 [[nodiscard]]
0145 void*
0146 allocate(size_t bytes, size_t alignment = alignof(std::max_align_t))
0147 {
0148 return ::operator new(bytes, do_allocate(bytes, alignment));
0149 }
0150
0151 void
0152 deallocate(void* p, size_t bytes, size_t alignment = alignof(std::max_align_t))
0153 {
0154 return do_deallocate(p, bytes, alignment);
0155 }
0156 #endif
0157
0158 #if !defined(BOOST_COBALT_NO_PMR)
0159 constexpr bool do_is_equal(memory_resource const& other) const noexcept override
0160 {
0161 return this == &other;
0162 }
0163 #endif
0164 };
0165
0166 inline sbo_resource * get_null_sbo_resource()
0167 {
0168 static sbo_resource empty_resource;
0169 return &empty_resource;
0170 }
0171
0172 template<typename T>
0173 struct sbo_allocator
0174 {
0175 template<typename U>
0176 sbo_allocator(sbo_allocator<U> alloc) : resource_(alloc.resource_)
0177 {
0178
0179 }
0180 using value_type = T;
0181 using size_type = std::size_t;
0182 using difference_type = std::ptrdiff_t;
0183 using propagate_on_container_move_assignment = std::true_type;
0184
0185 [[nodiscard]] constexpr T* allocate( std::size_t n )
0186 {
0187 BOOST_ASSERT(resource_);
0188 return static_cast<T*>(resource_->do_allocate(sizeof(T) * n, alignof(T)));
0189 }
0190
0191 constexpr void deallocate( T* p, std::size_t n )
0192 {
0193 BOOST_ASSERT(resource_);
0194 resource_->do_deallocate(p, sizeof(T) * n, alignof(T));
0195 }
0196 sbo_allocator(sbo_resource * resource) : resource_(resource) {}
0197
0198 sbo_resource * resource() const {return resource_;}
0199 private:
0200 template<typename>
0201 friend struct sbo_allocator;
0202
0203 sbo_resource * resource_{nullptr};
0204 };
0205
0206 }
0207
0208 #endif