list.h

#pragma once

#ifndef __LC_LIST_H
#define __LC_LIST_H

namespace LC
{

template<typename T>
class Node final
{
public:
Node(){}
explicit Node(Node<T>* next, T&& data) noexcept : _data(std::move(data)), _next(next) {}
explicit Node(Node<T>* next, const T& data) noexcept : _data(data), _next(next) {}
explicit Node(Node<T>* pre, Node<T>* next, const T& data) noexcept : _pre(pre), _data(data), _next(next) {}

public:
inline T& data() noexcept { return _data; }

template <typename... TArgs>
inline void emplace(TArgs&& ...args){ new(&_data)T(args...); }

inline void pack(T&& data) noexcept { _data = std::move(data); }
inline void pack(const T& data) noexcept { _data = data; }

inline void link(Node<T>* next) noexcept { _next = next; if(next != nullptr) next->_pre = this; }

public:
T _data;
Node<T>* _next = nullptr;
Node<T>* _pre = nullptr;
};

template<typename T>
class List
{
public:
List() {} //init head.
~List() { while(_size > 0) pop(); }

public:
//insert at beginning.
bool push(Node<T>* node)
{
if(node == nullptr)
return false;

Node<T>* first = _root._next;
_root.link(node);
node->link(first);
++_size;
return true;
}

bool insert(const T& data)
{
Node<T>* ins = alloc();
if(ins == nullptr)
return false;

ins->pack(data);

return push(ins);
};

bool insert(T&& data)
{
Node<T>* ins = alloc();

if(ins == nullptr)
return false;

ins->pack(std::move(data));

return push(ins);
};

template<typename... TArgs>
bool emplace(TArgs&& ...args)
{
Node<T>* ins = alloc(args...);
Node<T>* first = nullptr;
if(ins == nullptr) return false;

return push(ins);
}

inline Node<T>* begin() noexcept { return _root._next; }
inline void pop() noexcept
{
delete pop_not_free();
}

inline Node<T>* pop_not_free() noexcept
{
Node<T>* first = begin();
if(first == nullptr)
return nullptr;

Node<T>* second = first->_next;
_root.link(second);
--_size;

return first;
}

inline void erase(Node<T>* node) noexcept
{
if(node == nullptr || node->_pre == nullptr)
return;

node->_pre->link(node->_next);
--_size;
delete node;
}

inline size_t size() const noexcept { return _size; }

private:
template<typename... TArgs>
inline Node<T>* alloc(TArgs&& ...args)
{
Node<T>* ret = new(std::nothrow) Node<T>();
if(ret != nullptr) ret->emplace(args...);
return ret;
}

inline Node<T>* alloc() { return new(std::nothrow) Node<T>(); }

public:
Node<T> _root;
size_t _size = 0;
};

}; // namespace LC
#endif

mempool.h

/**
* @brief Memory pool, it's not thread-safe.
* @author Even
* @date 2021-06-07
*
*/

#pragma once

#ifndef __MEM_POOL_H_
#define __MEM_POOL_H_

#include <memory>

#include "./list/list.h"

namespace LC
{

inline static constexpr int ALIGN = 8;
inline static constexpr int BLOCK_MAX = 1024;
inline static constexpr int BLOCK_CAP = BLOCK_MAX / ALIGN;
inline static constexpr int BUCKET_CAP = BLOCK_MAX;

static_assert(ALIGN % 8 == 0);
static_assert(BLOCK_MAX % ALIGN == 0);

inline constexpr int __Align(int num_) noexcept { return (num_ + ALIGN - 1) & ~(ALIGN - 1); }
inline constexpr int __Sit(int index_) noexcept { return (index_ + 1) * ALIGN; }
inline constexpr int __Index(int num_) noexcept { return (num_ + ALIGN - 1) / ALIGN - 1; }

struct __Block
{
__Block* _next = nullptr;
};

struct __CacheBlock
{
__CacheBlock(){}
__CacheBlock(char* ptr, int size)
: _ptr(ptr), _size(size){}

std::unique_ptr<char[]> _ptr;
int _size = 0;
};

class __MemCache
{
private:
__MemCache(){}
~__MemCache(){}

private:
inline void Create(int size) { size = __Align(size); _cache.emplace((char*)malloc(size), size); }

//@return list.
struct ApplyRet{__Block* _b; __Block* _e;};
ApplyRet Apply(int& block_size, int& count);

private:
friend class MemPool;
List<__CacheBlock> _cache;
};

class MemPool
{
public:
MemPool();
~MemPool();

public:
static inline MemPool& Instance() noexcept { static MemPool ins_; return ins_; }

public:
void* Alloc(int size);
template<typename T> void* Alloc() { return Alloc(sizeof(T)); }

void Free(void* ptr, int size);
template<typename T> void Free(T* ptr) { Free(ptr, sizeof(T)); }

private:
void Fill(int index, int count);

private:
__MemCache _mem_cache;
__Block* _free[BLOCK_CAP];
};

#define __BUCKET_COUNT (64)

class MemPoolThreadSafe
{
public:
struct __ThreadMemPool
{
__ThreadMemPool(){}
__ThreadMemPool(int id, MemPool* pool) : _id(id), _pool(pool) {}
int _id = 0;
std::unique_ptr<MemPool> _pool;
};

public:
MemPoolThreadSafe() noexcept;
~MemPoolThreadSafe();

public:
inline static MemPoolThreadSafe& Instance() noexcept { static MemPoolThreadSafe ins_; return ins_; }
uint32_t GetThreadID();

public:
void* Alloc(int size);
void Free(void* ptr, int size);

template<typename T> void* Alloc() { return Alloc(sizeof(T)); }
template<typename T> void Free(T* ptr) { Free(ptr, sizeof(T)); }

private:
List<__ThreadMemPool> _forThreadMemPool[__BUCKET_COUNT];
};

#define G_MemoryPool LC::MemPoolThreadSafe::Instance()


};

#endif //!__MEM_POOL_H_

mempool.cpp

#include "lc_mempool.h"
#include <thread>

#define DEFAULT_EACH_BLOCK_STORE 16

namespace LC
{

__MemCache::ApplyRet __MemCache::Apply(int& block_size, int& count)
{
block_size = __Align(block_size);
int need_size = block_size * count;

{
int def_size = block_size * DEFAULT_EACH_BLOCK_STORE;

auto* node = _cache.begin();
if(node == nullptr || node->data()._size <= 0)
Create(need_size > def_size ? need_size : def_size);
}

__CacheBlock& block = _cache.begin()->data();
char* ptr = block._ptr.get();
int& size = block._size;

if(size <= block_size)
{
block_size = size;
count = 1;
return ApplyRet{(__Block*)(ptr), (__Block*)(ptr)};
}

__Block * list = nullptr,
* begin = nullptr,
* end = nullptr;

int ret_count = 0;
for (;size >= block_size;)
{
char* block_ptr = ptr + size - block_size;

if(list == nullptr)
{
list = (__Block*)(block_ptr);
begin = end = list;
}
else
{
list->_next = (__Block*)(block_ptr);
end = list = list->_next;
}

size -= block_size;
++ret_count;

if(ret_count >= count)
{
count = ret_count;
return ApplyRet{begin, end};
}
}
return ApplyRet{begin, end};
}

MemPool::MemPool()
{
std::memset(_free, 0, sizeof(_free));
}

MemPool::~MemPool()
{
}

void MemPool::Fill(int index, int count)
{
int size = __Sit(index);
auto ret = _mem_cache.Apply(size, count);

__Block*& p = _free[__Index(size)];
ret._e->_next = p;
p = ret._b;
}

void* MemPool::Alloc(int size)
{
if(size > BLOCK_MAX)
return malloc(size);

if(size <= 0)
return nullptr;

LABLE_ALLOC:
const int index = __Index(size);
__Block* block = _free[index];
if(block == nullptr)
{
Fill(index, DEFAULT_EACH_BLOCK_STORE);
goto LABLE_ALLOC;
}

block = _free[index];
_free[index] = block->_next;

return block;
}

void MemPool::Free(void* ptr, int size)
{
if(ptr == nullptr)
return;

if(size > BLOCK_MAX)
{
free(ptr);
return;
}

const int index = __Index(size);
__Block* block = (__Block*)(ptr);

block->_next = _free[index];
_free[index] = block;
}

MemPoolThreadSafe::MemPoolThreadSafe() noexcept
{

}

MemPoolThreadSafe::~MemPoolThreadSafe()
{

}

uint32_t MemPoolThreadSafe::GetThreadID()
{
static __thread uint32_t id = 0;
if(id != 0) return id;
id = std::hash<std::thread::id>()(std::this_thread::get_id());
return id;
}

void* MemPoolThreadSafe::Alloc(int size)
{
uint32_t id = GetThreadID();
uint32_t index = id % __BUCKET_COUNT;
auto& list = _forThreadMemPool[index];

auto* node = list.begin();
for (; node != nullptr; )
{
if(node->data()._id != id)
{
node = node->_next;
continue;
}
break;
}

if(node == nullptr)
{
list.emplace(id, new MemPool());
node = list.begin();
}

return node->data()._pool->Alloc(size);
}

void MemPoolThreadSafe::Free(void* ptr, int size)
{
if(ptr == nullptr)
return;

if(size <= 0)
{
free(ptr);
return;
}

uint32_t id = GetThreadID();
uint32_t index = id % __BUCKET_COUNT;

auto& list = _forThreadMemPool[index];
if(list.size() <= 0)
list.emplace(id, new MemPool());

auto* node = list.begin();
for (; node != nullptr; )
{
if(node->data()._id != id)
{
node = node->_next;
continue;
}

break;
}

if(node == nullptr)
exit(3);

return node->data()._pool->Free(ptr, size);
}

};