Загрузка данных
#pragma once
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <algorithm>
#include <new>
#include <utility>
#include <limits>
#include <cstddef>
#include <bit>
#include <cassert>
namespace mm
{
#ifdef __cpp_lib_hardware_interference_size
inline constexpr std::size_t cache_line = std::hardware_destructive_interference_size;
#else
inline constexpr std::size_t cache_line = 64;
#endif
class guard
{
protected:
struct alloc_info {
std::size_t packed_data;
static alloc_info pack(std::size_t size, std::size_t alignment) noexcept {
std::size_t log2_align = static_cast<std::size_t>(std::countr_zero(alignment));
std::size_t packed = size | (log2_align << 58);
return alloc_info{ packed };
}
std::size_t get_size() const noexcept {
return packed_data & ((1ULL << 58) - 1);
}
std::size_t get_alignment() const noexcept {
std::size_t log2_align = packed_data >> 58;
return 1ULL << log2_align;
}
};
static std::mutex& m_mm_guard_mtx() {
alignas(mm::cache_line) static std::mutex instance;
return instance;
}
alignas(mm::cache_line) inline static std::atomic<std::size_t> m_mm_bytes_allocated{0};
static std::unordered_map<void*, alloc_info>* m_allocated()
{
static std::unordered_map<void*, alloc_info>* instance = new std::unordered_map<void*, alloc_info>();
return instance;
}
private:
class cleaner
{
public:
~cleaner()
{
std::scoped_lock lock(m_mm_guard_mtx());
while (!m_allocated()->empty()) {
auto it = m_allocated()->begin();
void* ptr = it->first;
alloc_info info = it->second;
m_allocated()->erase(it);
if (info.get_alignment() > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
::operator delete(ptr, info.get_size(), std::align_val_t{info.get_alignment()});
} else {
::operator delete(ptr, info.get_size());
}
}
m_mm_bytes_allocated = 0;
}
};
inline static cleaner m_cleaner{};
public:
guard() noexcept {
static const bool initialized = []()
{
std::scoped_lock lock(m_mm_guard_mtx());
m_allocated()->reserve(1024);
return true;
}();
}
~guard() = default;
friend inline std::size_t allocated_bytes() noexcept;
};
inline std::size_t allocated_bytes() noexcept
{
return mm::guard::m_mm_bytes_allocated;
}
template <typename Ty>
class guard_alloc : public mm::guard
{
public:
using value_type = Ty;
guard_alloc() noexcept = default;
template <typename U>
guard_alloc(const mm::guard_alloc<U> &) noexcept {};
[[nodiscard]] value_type *allocate(std::size_t n)
{
static_assert(sizeof(value_type) != 0, "cannot allocate incomplete types");
if (n == 0)
return nullptr;
#define __ALLOC_LIMIT_ASSUME_COND (n <= std::numeric_limits<std::size_t>::max() / sizeof(value_type))
assert(__ALLOC_LIMIT_ASSUME_COND && "allocation size overflow");
#if defined(__cpp_attributes) && __has_cpp_attribute(assume)
[[assume(__ALLOC_LIMIT_ASSUME_COND)]];
#elif defined(_MSC_VER)
__assume(__ALLOC_LIMIT_ASSUME_COND);
#elif defined(__GNUC__) || defined(__clang__)
if (!__ALLOC_LIMIT_ASSUME_COND)
__builtin_unreachable();
#endif
std::size_t alloc_bytes = n * sizeof(value_type);
void* alloc_res = nullptr;
if (alignof(value_type) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
alloc_res = ::operator new(alloc_bytes, std::align_val_t{alignof(value_type)});
} else {
alloc_res = ::operator new(alloc_bytes);
}
try {
std::scoped_lock lock(m_mm_guard_mtx());
m_allocated()->insert_or_assign(alloc_res, alloc_info::pack(alloc_bytes, alignof(value_type)));
} catch(...) {
::operator delete(alloc_res, std::align_val_t{alignof(value_type)});
throw;
}
m_mm_bytes_allocated.fetch_add(alloc_bytes, std::memory_order_relaxed);
return static_cast<value_type *>(alloc_res);
}
void deallocate(value_type *p, std::size_t n) noexcept
{
if (!p || n == 0)
return;
void *target_ptr = static_cast<void *>(p);
alloc_info info{};
bool was_erased = false;
{
std::scoped_lock lock(m_mm_guard_mtx());
auto it = m_allocated()->find(target_ptr);
if (it != m_allocated()->end()) {
info = it->second;
m_allocated()->erase(it);
was_erased = true;
}
}
if(was_erased) {
std::size_t dealloc_bytes = info.get_size();
std::size_t alignment = info.get_alignment();
if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
::operator delete(target_ptr, dealloc_bytes, std::align_val_t{alignment});
} else {
::operator delete(target_ptr, dealloc_bytes);
}
m_mm_bytes_allocated.fetch_sub(dealloc_bytes, std::memory_order_relaxed);
}
}
template <typename U>
friend bool operator==(const guard_alloc&, const guard_alloc<U>&) noexcept { return true; }
template <typename U>
friend bool operator!=(const guard_alloc&, const guard_alloc<U>&) noexcept { return false; }
};
namespace ga
{
template<typename MakeTy, typename... Args>
[[nodiscard]] MakeTy* make(Args&&... args) {
auto alloc = guard_alloc<MakeTy>();
MakeTy *ptr = alloc.allocate(1);
if(ptr)
::new (static_cast<void *>(ptr)) MakeTy(std::forward<Args>(args)...);
return ptr;
}
template <typename DropTy>
void drop(DropTy *&ptr) noexcept
{
if (!ptr)
return;
ptr->~DropTy();
auto alloc = guard_alloc<DropTy>();
alloc.deallocate(ptr, 1);
ptr = nullptr;
}
} // namespace ga
} // namespace mm