/** * \file libpmr/new.h * \author mutouyun (orz@orzz.org) * \brief Global object creation function. * \date 2024-01-01 */ #pragma once #include #include #include #include "libimp/aligned.h" #include "libimp/uninitialized.h" #include "libimp/byte.h" #include "libimp/detect_plat.h" #include "libpmr/def.h" #include "libpmr/block_pool.h" #include "libpmr/memory_resource.h" LIBPMR_NAMESPACE_BEG_ constexpr inline std::size_t regular_level(std::size_t s) noexcept { return (s <= 128 ) ? 0 : (s <= 1024 ) ? 1 : (s <= 8192 ) ? 2 : (s <= 65536) ? 3 : 4; } constexpr inline std::size_t regular_sizeof_impl(std::size_t l, std::size_t s) noexcept { return (l == 0) ? std::max(::LIBIMP::round_up(s, 8), regular_head_size) : (l == 1) ? ::LIBIMP::round_up(s, 128) : (l == 2) ? ::LIBIMP::round_up(s, 1024) : (l == 3) ? ::LIBIMP::round_up(s, 8192) : (std::numeric_limits::max)(); } constexpr inline std::size_t regular_sizeof(std::size_t s) noexcept { return regular_sizeof_impl(regular_level(s), s); } template constexpr inline std::size_t regular_sizeof() noexcept { return regular_sizeof(regular_head_size + sizeof(T)); } class block_pool_like { public: virtual ~block_pool_like() noexcept = default; virtual void *allocate() noexcept = 0; virtual void deallocate(void *p) noexcept = 0; }; inline std::unordered_map &get_block_pool_map() noexcept { thread_local std::unordered_map instances; return instances; } template class block_pool_resource; template <> class block_pool_resource<0, 0> : public block_pool<0, 0> , public block_pool_like { void *allocate() noexcept override { return nullptr; } public: void deallocate(void *p) noexcept override { block_pool<0, 0>::deallocate(p); } }; template class block_pool_resource : public block_pool , public block_pool_like { using base_t = block_pool; void *allocate() noexcept override { return base_t::allocate(); } void deallocate(void *p) noexcept override { base_t::deallocate(p); } public: static block_pool_resource *get() noexcept; using base_t::base_t; void *allocate(std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept { void *p = base_t::allocate(); p = ::LIBIMP::construct(p, BlockSize); return reinterpret_cast<::LIBIMP::byte *>(p) + regular_head_size; } void deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/ = alignof(std::max_align_t)) noexcept { p = reinterpret_cast<::LIBIMP::byte *>(p) - regular_head_size; auto r_size = *static_cast(p); if (r_size <= BlockSize) { base_t::deallocate(p); return; } auto &map = get_block_pool_map(); auto it = map.find(r_size); if ((it == map.end()) || (it->second == nullptr)) LIBIMP_TRY { // If the corresponding memory resource cannot be found, // create a temporary general-purpose block pool to deallocate memory. it = map.emplace(r_size, new block_pool_resource<0, 0>).first; } LIBIMP_CATCH(...) { // If the memory resource cannot be created, // store the pointer directly to avoid leakage. base_t::deallocate(p); return; } it->second->deallocate(p); } }; template auto block_pool_resource::get() noexcept -> block_pool_resource * { auto &map = get_block_pool_map(); thread_local block_pool_resource *pi = nullptr; if (pi != nullptr) { return pi; } auto it = map.find(BlockSize); if ((it != map.end()) && (it->second != nullptr)) { auto *bp = static_cast *>( dynamic_cast *>(it->second)); if (bp == nullptr) { return nullptr; } thread_local block_pool_resource instance(std::move(*bp)); delete static_cast *>(bp); pi = &instance; } else { thread_local block_pool_resource instance; pi = &instance; } LIBIMP_TRY { map.emplace(BlockSize, pi); } LIBIMP_CATCH(...) { return nullptr; } return pi; } template class regular_resource : public new_delete_resource {}; template class regular_resource : public block_pool_resource {}; template class regular_resource : public block_pool_resource {}; template class regular_resource : public block_pool_resource {}; template class regular_resource : public block_pool_resource {}; template T *new$(A &&... args) noexcept { auto *mem_res = regular_resource()>::get(); if (mem_res == nullptr) return nullptr; return ::LIBIMP::construct(mem_res->allocate(sizeof(T), alignof(T)), std::forward(args)...); } template void delete$(T *p) noexcept { if (p == nullptr) return; ::LIBIMP::destroy(p); auto *mem_res = regular_resource()>::get(); if (mem_res == nullptr) return; #if defined(LIBIMP_CC_MSVC_2015) mem_res->deallocate(p, sizeof(T)); #else mem_res->deallocate(p, sizeof(T), alignof(T)); #endif } LIBPMR_NAMESPACE_END_