server/game/src/pool.h

280 lines
6.4 KiB
C++

#ifndef __INC_METIN_II_GAME_POOL_H__
#define __INC_METIN_II_GAME_POOL_H__
// Neither error-checking nor watermarking here.
// Definitely not thread-safe.
// In order to debug the heap memory usage, activate DebugAllocator by defining
// DEBUG_ALLOC.
#ifdef M2_USE_POOL
template<typename T>
struct PoolNode {
T* block;
PoolNode* next;
};
template<typename T>
struct PoolAllocation {
T* chunk;
size_t num_blocks;
PoolNode<T>* nodes;
};
template<typename T>
struct PoolDetail {
typedef T* PointerType;
typedef T* ArithmeticPointerType;
static PointerType Alloc(size_t num) {
return new T[num];
}
static void Free(PointerType p) {
delete[] p;
}
};
// Explicit specialization of PoolDetail for raw non-initialized memory blocks.
template<>
struct PoolDetail<void> {
typedef void* PointerType;
typedef char* ArithmeticPointerType;
static PointerType Alloc(size_t num) {
return ::malloc(num);
}
static void Free(PointerType p) {
::free(p);
}
};
// Generic grow-only pool of arrays.
// Non-void template parameter type T should provide no-arg default constructor.
template<typename T>
class ArrayPool {
public:
ArrayPool(size_t array_size, size_t initial_capacity = 0)
: free_(NULL),
array_size_(array_size),
capacity_(0),
alloc_count_(0),
alloc_index_of_last_release_(0) {
assert(array_size_ != 0);
if (initial_capacity != 0) {
Reserve(initial_capacity);
}
}
~ArrayPool() {
CleanUp();
}
// Acquires an available array from the pool.
T* Acquire() {
if (free_ == NULL) {
if (Stretch(capacity_) == false) {
return NULL;
}
}
assert(free_ != NULL);
PointerType p = free_->block;
free_ = free_->next;
return p;
}
// Releases the specified array and returns it to the pool.
void Release(T* p) {
if (p == NULL) {
return;
}
size_t index = alloc_index_of_last_release_;
for (size_t i = 0; i < alloc_count_; ++i, ++index) {
if (index >= alloc_count_) {
index = 0;
}
AllocationType& alloc = allocated_[index];
ArithmeticPointerType ptr = static_cast<ArithmeticPointerType>(p);
ArithmeticPointerType begin = static_cast<ArithmeticPointerType>(
alloc.chunk);
ArithmeticPointerType end = begin + (array_size_ * alloc.num_blocks);
if (begin <= ptr && ptr < end) {
size_t node_index = (ptr - begin) / array_size_;
NodeType* node = alloc.nodes + node_index;
assert(node->block == p);
if (node->block != p) {
break;
}
node->next = free_;
free_ = node;
alloc_index_of_last_release_ = index;
break;
}
}
}
// Requests that the capacity of the pool be enough to hold at least n arrays.
void Reserve(size_t n) {
if (n <= capacity_) {
return;
}
Stretch(n - capacity_);
}
// Frees all the memory blocks allocated by the pool.
void CleanUp() {
if (alloc_count_ == 0) {
return;
}
while (alloc_count_ != 0) {
AllocationType& alloc = allocated_[--alloc_count_];
DetailType::Free(alloc.chunk);
delete[] alloc.nodes;
}
capacity_ = 0;
free_ = NULL;
}
// Gets the size of an array in the pool.
size_t array_size() const { return array_size_; }
// Gets the current total capacity, in number of arrays, of the pool.
size_t capacity() const { return capacity_; }
private:
typedef PoolNode<T> NodeType;
typedef PoolAllocation<T> AllocationType;
typedef PoolDetail<T> DetailType;
typedef typename DetailType::PointerType PointerType;
typedef typename DetailType::ArithmeticPointerType ArithmeticPointerType;
static const size_t kMaxAllocCount = sizeof(size_t) * CHAR_BIT;
bool Stretch(size_t increment) {
if (increment == 0) {
++increment; // minimum increment 1
}
if (alloc_count_ >= kMaxAllocCount) {
return false;
}
ArithmeticPointerType p = static_cast<ArithmeticPointerType>(
DetailType::Alloc(array_size_ * increment));
assert(p != NULL);
if (p == NULL) {
return false;
}
NodeType* node = new NodeType[increment];
assert(node != NULL);
if (node == NULL) {
DetailType::Free(p);
return false;
}
AllocationType& alloc = allocated_[alloc_count_++];
alloc.chunk = p;
alloc.num_blocks = increment;
alloc.nodes = node;
NodeType* tail = free_;
NodeType** link = &free_;
for (size_t i = 0; i < increment ; ++i, ++node, p += array_size_) {
node->block = p;
*link = node;
link = &(node->next);
}
*link = tail;
capacity_ += increment;
return true;
}
NodeType* free_;
size_t array_size_;
size_t capacity_;
AllocationType allocated_[kMaxAllocCount];
size_t alloc_count_;
size_t alloc_index_of_last_release_;
// No copy
ArrayPool(const ArrayPool&);
void operator=(const ArrayPool&);
};
// Special alias for the pool of raw(non-typed) non-initialized memory blocks.
typedef ArrayPool<void> Pool;
// Variable-length memory pool backed by multiple fixed-length pools.
class MemoryPool {
public:
MemoryPool() {}
~MemoryPool() {
PoolMapType::iterator it = pools_.begin(), end = pools_.end();
for ( ; it != end; ++it) {
delete (it->second);
}
}
// Acquires a memory block of specified size from a pool.
void* Acquire(size_t size) {
Pool* pool;
PoolMapType::iterator it = pools_.find(size);
if (it != pools_.end()) {
pool = it->second;
} else {
pool = new Pool(size);
pools_.insert(PoolMapType::value_type(size, pool));
}
return pool->Acquire();
}
// Releases the specified memory block and returns it to a pool.
void Release(void* p, size_t size) {
PoolMapType::iterator it = pools_.find(size);
if (it == pools_.end()) {
return;
}
Pool* pool = it->second;
pool->Release(p);
}
private:
typedef std::unordered_map<size_t, Pool*> PoolMapType;
PoolMapType pools_;
};
// Grow-only simple object pool, relying on ctor/dtor.
template<class T> // T should provide no-arg default constructor
class ObjectPool {
public:
ObjectPool(size_t initial_capacity = 0)
: pool_(sizeof(T), initial_capacity) {}
~ObjectPool() {}
// Constructs a new object from the pool.
T* Construct() {
void* p = pool_.Acquire();
if (p == NULL) {
return NULL;
}
return new (p) T();
}
// Destroys the specified object and returns it to the pool.
void Destroy(T* p) {
if (p == NULL) {
return;
}
p->~T();
pool_.Release(p);
}
// Requests that the pool capacity be enough to hold at least n objects.
void Reserve(size_t n) {
pool_.Reserve(n);
}
private:
Pool pool_;
// No copy
ObjectPool(const ObjectPool&);
void operator=(const ObjectPool&);
};
#endif
#endif // __INC_METIN_II_GAME_POOL_H__