LCOV - code coverage report
Current view: top level - core/core/memory - SPMemFunction.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 96 96 100.0 %
Date: 2024-05-12 00:16:13 Functions: 2613 3426 76.3 %

          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_ */

Generated by: LCOV version 1.14