Line data Source code
1 : /**
2 : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_CORE_MEMORY_SPMEMFUNCTION_H_
25 : #define STAPPLER_CORE_MEMORY_SPMEMFUNCTION_H_
26 :
27 : #include "SPMemAlloc.h"
28 :
29 : namespace STAPPLER_VERSIONIZED stappler::memory {
30 :
31 : // Function - реализация std::function, использующая память из pool_t
32 : // some sources from https://github.com/prograholic/blog/blob/master/cxx_function/main.cpp
33 :
34 : template <typename, typename, typename = void>
35 : struct check_signature : std::false_type {};
36 :
37 : template <typename Func, typename Ret, typename... Args>
38 : struct check_signature<Func, Ret(Args...),
39 : typename std::enable_if<
40 : std::is_convertible<
41 : decltype(std::declval<Func>()(std::declval<Args>()...)),
42 : Ret
43 : >::value, void>::type>
44 : : std::true_type {};
45 :
46 : template <typename UnusedType>
47 : class function;
48 :
49 : template <typename ReturnType, typename ... ArgumentTypes>
50 : class function <ReturnType (ArgumentTypes ...)> : public AllocPool {
51 : public:
52 : using signature_type = ReturnType (ArgumentTypes ...);
53 : using allocator_type = Allocator<void *>;
54 :
55 64042 : ~function() { clear(); }
56 :
57 62872 : function(const allocator_type &alloc = allocator_type()) noexcept : mAllocator(alloc), mCallback(nullptr) { }
58 :
59 52801 : function(nullptr_t, const allocator_type &alloc = allocator_type()) noexcept : mAllocator(alloc), mCallback(nullptr) { }
60 :
61 3574 : function &operator= (nullptr_t) noexcept { clear(); mCallback = nullptr; return *this; }
62 :
63 325 : function(const function & other, const allocator_type &alloc = allocator_type()) noexcept : mAllocator(alloc) {
64 325 : mCallback = other.mCallback;
65 325 : if (mCallback) {
66 250 : mCallback()->copy(other.mBuffer.data(), mAllocator, mBuffer.data());
67 : }
68 325 : }
69 :
70 566 : function & operator = (const function & other) noexcept {
71 566 : clear();
72 566 : mCallback = other.mCallback;
73 566 : if (mCallback) {
74 566 : mCallback()->copy(other.mBuffer.data(), mAllocator, mBuffer.data());
75 : }
76 566 : return *this;
77 : }
78 :
79 7865 : function(function && other, const allocator_type &alloc = allocator_type()) noexcept : mAllocator(alloc) {
80 7865 : mCallback = other.mCallback;
81 7865 : if (mCallback) {
82 6280 : if (other.mAllocator == mAllocator) {
83 6155 : mCallback()->move(other.mBuffer.data(), mAllocator, mBuffer.data());
84 : } else {
85 125 : mCallback()->copy(other.mBuffer.data(), mAllocator, mBuffer.data());
86 : }
87 : }
88 7865 : }
89 :
90 2201 : function & operator = (function && other) noexcept {
91 2201 : clear();
92 2201 : mCallback = other.mCallback;
93 2201 : if (mCallback) {
94 2201 : if (other.mAllocator == mAllocator) {
95 2151 : mCallback()->move(other.mBuffer.data(), mAllocator, mBuffer.data());
96 : } else {
97 50 : mCallback()->copy(other.mBuffer.data(), mAllocator, mBuffer.data());
98 : }
99 : }
100 2201 : return *this;
101 : }
102 :
103 : template <typename FunctionT,
104 : class = typename std::enable_if<!std::is_same<
105 : typename std::remove_cv<typename std::remove_reference<FunctionT>::type>::type,
106 : function<ReturnType (ArgumentTypes ...)
107 : >>::value>::type>
108 12904 : function(FunctionT && f, const allocator_type &alloc = allocator_type()) noexcept
109 12904 : : mAllocator(alloc) {
110 12904 : mCallback = makeFreeFunction(std::forward<FunctionT>(f), mAllocator, mBuffer.data());
111 12905 : }
112 :
113 : template <typename FunctionT>
114 3548 : function & operator= (FunctionT &&f) noexcept {
115 3548 : clear();
116 3548 : mCallback = makeFreeFunction(std::forward<FunctionT>(f), mAllocator, mBuffer.data());
117 3548 : return *this;
118 : }
119 :
120 609108 : ReturnType operator () (ArgumentTypes ... args) const {
121 609108 : return mCallback()->invoke(mBuffer.data(), std::forward<ArgumentTypes>(args) ...);
122 : }
123 :
124 : constexpr bool operator == (nullptr_t) const noexcept { return mCallback == nullptr; }
125 :
126 359700 : constexpr bool operator != (nullptr_t) const noexcept { return mCallback != nullptr; }
127 :
128 411571 : constexpr explicit operator bool () const noexcept { return mCallback != nullptr; }
129 :
130 : constexpr bool operator == (const function & other) const noexcept {
131 : return mAllocator == other.mAllocator && mCallback == other.mCallback && mBuffer == other.mBuffer;
132 : }
133 :
134 : constexpr bool operator != (const function & other) const noexcept {
135 : return mAllocator != other.mAllocator || mCallback != other.mCallback || mBuffer != other.mBuffer;
136 : }
137 :
138 400 : const allocator_type &get_allocator() const { return mAllocator; }
139 :
140 : private:
141 : static constexpr auto OptBufferSize = 16;
142 :
143 : using invoke_pointer = ReturnType (*) (const void *, ArgumentTypes ...);
144 : using destroy_pointer = void (*) (void *);
145 : using copy_pointer = void * (*) (const void *, allocator_type &, uint8_t *);
146 : using move_pointer = void * (*) (void *, allocator_type &, uint8_t *);
147 :
148 : struct functor_traits {
149 : invoke_pointer invoke;
150 : destroy_pointer destroy;
151 : copy_pointer copy;
152 : move_pointer move;
153 : };
154 :
155 : using traits_callback = functor_traits * (*) ();
156 :
157 : template <typename FunctionT>
158 677406 : static functor_traits *makeFunctionTraits() noexcept {
159 : using BaseType = typename std::remove_cv<typename std::remove_reference<FunctionT>::type>::type;
160 : using BaseTypePtr = BaseType *;
161 : if constexpr(sizeof(BaseType) <= OptBufferSize) {
162 : static functor_traits traits{
163 407190 : [] (const void* arg, ArgumentTypes ... args) -> ReturnType {
164 407190 : return (*static_cast<const BaseType *>(arg))(std::forward<ArgumentTypes>(args) ...);
165 : },
166 31850 : [] (void *arg) {
167 15925 : if (arg) { (*static_cast<BaseType *>(arg)).~BaseType(); }
168 : },
169 1024 : [] (const void *arg, allocator_type &alloc, uint8_t *buf) -> void * {
170 1024 : return new (buf) BaseType(*static_cast<const BaseType *>(arg));
171 : },
172 7381 : [] (void *arg, allocator_type &alloc, uint8_t *buf) -> void * {
173 7381 : return new (buf) BaseType(std::move(*static_cast<BaseType *>(arg)));
174 : },
175 : };
176 :
177 431518 : return &traits;
178 : } else {
179 : static functor_traits traits{
180 242933 : [] (const void* arg, ArgumentTypes ... args) -> ReturnType {
181 242933 : return (*(*static_cast<const BaseTypePtr *>(arg)))(std::forward<ArgumentTypes>(args) ...);
182 : },
183 3884 : [] (void *arg) {
184 1942 : auto ptr = *static_cast<BaseTypePtr *>(arg);
185 1942 : if (ptr) {
186 595 : ptr->~BaseType();
187 949 : new (arg) (const BaseType *)(nullptr);
188 : }
189 : },
190 20 : [] (const void *arg, allocator_type &alloc, uint8_t *buf) -> void * {
191 20 : Allocator<BaseType> ialloc = alloc;
192 20 : auto mem = ialloc.allocate(1);
193 20 : ialloc.construct(mem, *(*static_cast<const BaseTypePtr *>(arg)));
194 20 : return new (buf) (const BaseType *)(mem);
195 : },
196 993 : [] (void *arg, allocator_type &alloc, uint8_t *buf) -> void * {
197 993 : auto ret = new (buf) (const BaseType *)((*static_cast<BaseTypePtr *>(arg)));
198 993 : new (arg) (const BaseType *)(nullptr);
199 993 : return ret;
200 : },
201 : };
202 :
203 245888 : return &traits;
204 : }
205 : }
206 :
207 : template <typename FunctionT>
208 16453 : static traits_callback makeFreeFunction(FunctionT && f, allocator_type &alloc, uint8_t *buf) noexcept {
209 : using BaseType = typename std::remove_cv<typename std::remove_reference<FunctionT>::type>::type;
210 : if constexpr(sizeof(BaseType) <= OptBufferSize) {
211 14769 : new (buf) BaseType(std::forward<FunctionT>(f));
212 : } else {
213 1684 : Allocator<BaseType> ialloc = alloc;
214 1684 : auto mem = ialloc.allocate(1);
215 :
216 1684 : memory::pool::push(alloc);
217 1684 : new (mem) BaseType(std::forward<FunctionT>(f));
218 1684 : memory::pool::pop();
219 :
220 1684 : new (buf) (const BaseType *)(mem);
221 : }
222 16453 : return &makeFunctionTraits<FunctionT>;
223 : }
224 :
225 73796 : void clear() {
226 73796 : if (mCallback) {
227 17604 : auto t = mCallback();
228 17604 : t->destroy(mBuffer.data());
229 17604 : mCallback = nullptr;
230 : }
231 73796 : }
232 :
233 : allocator_type mAllocator;
234 : traits_callback mCallback = nullptr;
235 : std::array<uint8_t, OptBufferSize> mBuffer;
236 : };
237 :
238 :
239 : template <typename UnusedType>
240 : class callback;
241 :
242 : // Modern version. inspired by http://bannalia.blogspot.com/2016/07/passing-capturing-c-lambda-functions-as.html
243 : template <typename ReturnType, typename ... ArgumentTypes>
244 : class callback <ReturnType (ArgumentTypes ...)> : public AllocPool {
245 : public:
246 : using signature_type = ReturnType (ArgumentTypes ...);
247 :
248 227783221 : ~callback() {
249 : // functor is not owned, so, no cleanup
250 227783221 : }
251 :
252 52591 : callback(nullptr_t) noexcept : mFunctor(nullptr), mCallback(nullptr) { }
253 :
254 : callback& operator=(nullptr_t) noexcept { mFunctor = nullptr; mCallback = nullptr; }
255 :
256 : template <typename FunctionT>
257 227396798 : callback(const FunctionT & f) noexcept
258 1157267278 : : mFunctor(&f), mCallback([] (const void* arg, ArgumentTypes ... args) {
259 929870726 : return (*static_cast<const FunctionT *>(arg))(std::forward<ArgumentTypes>(args) ...);
260 227429127 : }) { }
261 :
262 : template <typename FunctionT>
263 : callback & operator= (const FunctionT &f) noexcept {
264 : mFunctor = &f;
265 : mCallback = [] (const void* arg, ArgumentTypes ... args) {
266 : return (*static_cast<const FunctionT *>(arg))(std::forward<ArgumentTypes>(args) ...);
267 : };
268 : }
269 :
270 : template <typename FunctionType, typename ClassType>
271 : callback(FunctionType ClassType::* f) noexcept
272 : : mFunctor(f) {
273 : mCallback = makeMemberFunction<FunctionType, ArgumentTypes ...>(f);
274 : }
275 :
276 : template <typename FunctionType, typename ClassType>
277 : callback & operator= (FunctionType ClassType::* f) noexcept {
278 : mFunctor = f;
279 : mCallback = makeMemberFunction<FunctionType, ArgumentTypes ...>(f);
280 : }
281 :
282 : callback(const callback & other) noexcept = delete;
283 : callback & operator = (const callback & other) noexcept = delete;
284 :
285 : callback(callback && other) noexcept = delete;
286 : callback & operator = (callback && other) noexcept = delete;
287 :
288 930041618 : ReturnType operator () (ArgumentTypes ... args) const {
289 930041618 : return mCallback(mFunctor, std::forward<ArgumentTypes>(args) ...);
290 : }
291 :
292 : constexpr bool operator==(nullptr_t) const noexcept { return mFunctor == nullptr || mCallback == nullptr; }
293 :
294 44712 : constexpr bool operator!=(nullptr_t) const noexcept { return mFunctor != nullptr && mCallback != nullptr; }
295 :
296 428777 : constexpr explicit operator bool () const noexcept { return mFunctor != nullptr && mCallback != nullptr; }
297 :
298 : private:
299 : using FunctionPointer = ReturnType (*) (const void *, ArgumentTypes ...);
300 :
301 : template <typename FunctionType, typename ClassType, typename ... RestArgumentTypes>
302 : static FunctionPointer makeMemberFunction(FunctionType ClassType::* f) {
303 : return [] (const void* arg, ClassType && obj, RestArgumentTypes ... args) { // note thunk is captureless
304 : return (obj.*static_cast<const FunctionType ClassType::*>(arg))(std::forward<RestArgumentTypes>(args) ...);
305 : };
306 : }
307 :
308 : const void * mFunctor;
309 : FunctionPointer mCallback;
310 : };
311 :
312 : }
313 :
314 : #endif /* STAPPLER_CORE_MEMORY_SPMEMFUNCTION_H_ */
|