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

source code of boost/libs/cobalt/include/boost/cobalt/detail/sbo_resource.hpp