(svn r16378) -Codechange: replace OldPool with simpler Pool. Compilation time, binary size and run time (with asserts disabled) should be improved
parent
7eb14aa49f
commit
50624d5c0f
@ -0,0 +1,284 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file pool.hpp Defintion of Pool, structure used to access PoolItems, and PoolItem, base structure for Vehicle, Town, and other indexed items. */
|
||||
|
||||
#ifndef POOL_HPP
|
||||
#define POOL_HPP
|
||||
|
||||
template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size>
|
||||
struct Pool {
|
||||
static const size_t MAX_SIZE = Tmax_size; ///< Make template parameter accessible from outside
|
||||
|
||||
const char * const name; ///< Name of this pool
|
||||
|
||||
size_t size; ///< Current allocated size
|
||||
size_t first_free; ///< No item with index lower than this is free (doesn't say anything about this one!)
|
||||
size_t first_unused; ///< This and all higher indexes are free (doesn't say anything about first_unused-1 !)
|
||||
size_t items; ///< Number of used indexes (non-NULL)
|
||||
|
||||
bool cleaning; ///< True if cleaning pool (deleting all items)
|
||||
|
||||
Titem **data; ///< Pointer to array of pointers to Titem
|
||||
|
||||
/** Constructor */
|
||||
Pool(const char *name);
|
||||
/** Destroys all items in the pool and resets all member variables */
|
||||
void CleanPool();
|
||||
|
||||
/**
|
||||
* Returs Titem with given index
|
||||
* @param index of item to get
|
||||
* @return pointer to Titem
|
||||
* @pre index < this->first_unused
|
||||
*/
|
||||
FORCEINLINE Titem *Get(size_t index)
|
||||
{
|
||||
assert(index < this->first_unused);
|
||||
return this->data[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether given index can be used to get valid (non-NULL) Titem
|
||||
* @param index index to examine
|
||||
* @return true if PoolItem::Get(index) will return non-NULL pointer
|
||||
*/
|
||||
FORCEINLINE bool IsValidID(size_t index)
|
||||
{
|
||||
return index < this->first_unused && this->Get(index) != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether we can allocate 'n' items
|
||||
* @param n number of items we want to allocate
|
||||
* @return true if 'n' items can be allocated
|
||||
*/
|
||||
FORCEINLINE bool CanAllocate(size_t n = 1)
|
||||
{
|
||||
return this->items <= Tmax_size - n;
|
||||
}
|
||||
|
||||
/** Base class for all PoolItems */
|
||||
template <struct Pool<Titem, Tindex, Tgrowth_step, Tmax_size> *Tpool>
|
||||
struct PoolItem {
|
||||
Tindex index; ///< Index of this pool item
|
||||
|
||||
/**
|
||||
* Allocates space for new Titem
|
||||
* @param size size of Titem
|
||||
* @return pointer to allocated memory
|
||||
* @note can never fail (return NULL), use CanAllocate() to check first!
|
||||
*/
|
||||
FORCEINLINE void *operator new(size_t size)
|
||||
{
|
||||
return Tpool->GetNew(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks Titem as free. Its memory is released
|
||||
* @param p memory to free
|
||||
* @note the item has to be allocated in the pool!
|
||||
*/
|
||||
FORCEINLINE void operator delete(void *p)
|
||||
{
|
||||
Titem *pn = (Titem *)p;
|
||||
assert(pn == Tpool->Get(pn->index));
|
||||
Tpool->FreeItem(pn->index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates space for new Titem with given index
|
||||
* @param size size of Titem
|
||||
* @param index index of item
|
||||
* @return pointer to allocated memory
|
||||
* @note can never fail (return NULL), use CanAllocate() to check first!
|
||||
* @pre index has to be unused! Else it will crash
|
||||
*/
|
||||
FORCEINLINE void *operator new(size_t size, size_t index)
|
||||
{
|
||||
return Tpool->GetNew(size, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes item with given index.
|
||||
* @param p Titem memory to release
|
||||
* @param index index of item
|
||||
* @note never use this! Only for internal use
|
||||
* (called automatically when constructor of Titem uses throw())
|
||||
*/
|
||||
FORCEINLINE void operator delete(void *p, size_t index)
|
||||
{
|
||||
assert(p == Tpool->Get(index));
|
||||
Tpool->FreeItem(index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates space for new Titem at given memory address
|
||||
* @param size size of Titem
|
||||
* @param ptr where are we allocating the item?
|
||||
* @return pointer to allocated memory (== ptr)
|
||||
* @note use of this is strongly discouraged
|
||||
* @pre the memory must not be allocated in the Pool!
|
||||
*/
|
||||
FORCEINLINE void *operator new(size_t size, void *ptr)
|
||||
{
|
||||
for (size_t i = 0; i < Tpool->first_unused; i++) {
|
||||
/* Don't allow creating new objects over existing.
|
||||
* Even if we called the destructor and reused this memory,
|
||||
* we don't know whether 'size' and size of currently allocated
|
||||
* memory are the same (because of possible inheritance).
|
||||
* Use { size_t index = item->index; delete item; new (index) item; }
|
||||
* instead to make sure destructor is called and no memory leaks. */
|
||||
assert(ptr != Tpool->data[i]);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes item that was allocated by the function above
|
||||
* @param p Titem memory to release
|
||||
* @param ptr parameter given to operator new
|
||||
* @note never use this! Only for internal use
|
||||
* (called automatically when constructor of Titem uses throw())
|
||||
*/
|
||||
FORCEINLINE void operator delete(void *p, void *ptr)
|
||||
{
|
||||
assert(p == ptr);
|
||||
for (size_t i = 0; i < Tpool->first_unused; i++) {
|
||||
assert(ptr != Tpool->data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function() */
|
||||
|
||||
/**
|
||||
* Tests whether we can allocate 'n' items
|
||||
* @param n number of items we want to allocate
|
||||
* @return true if 'n' items can be allocated
|
||||
*/
|
||||
static FORCEINLINE bool CanAllocateItem(size_t n = 1)
|
||||
{
|
||||
return Tpool->CanAllocate(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current state of pool cleaning - yes or no
|
||||
* @return true iff we are cleaning the pool now
|
||||
*/
|
||||
static FORCEINLINE bool CleaningPool()
|
||||
{
|
||||
return Tpool->cleaning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether given index can be used to get valid (non-NULL) Titem
|
||||
* @param index index to examine
|
||||
* @return true if PoolItem::Get(index) will return non-NULL pointer
|
||||
*/
|
||||
static FORCEINLINE bool IsValidID(size_t index)
|
||||
{
|
||||
return Tpool->IsValidID(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returs Titem with given index
|
||||
* @param index of item to get
|
||||
* @return pointer to Titem
|
||||
* @pre index < this->first_unused
|
||||
*/
|
||||
static FORCEINLINE Titem *Get(size_t index)
|
||||
{
|
||||
return Tpool->Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returs Titem with given index
|
||||
* @param index of item to get
|
||||
* @return pointer to Titem
|
||||
* @note returns NULL for invalid index
|
||||
*/
|
||||
static FORCEINLINE Titem *GetIfValid(size_t index)
|
||||
{
|
||||
return index < Tpool->first_unused ? Tpool->Get(index) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first unused index. Useful when iterating over
|
||||
* all pool items.
|
||||
* @return first unused index
|
||||
*/
|
||||
static FORCEINLINE size_t GetPoolSize()
|
||||
{
|
||||
return Tpool->first_unused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of valid items in the pool
|
||||
* @return number of valid items in the pool
|
||||
*/
|
||||
static FORCEINLINE size_t GetNumItems()
|
||||
{
|
||||
return Tpool->items;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static const size_t NO_FREE_ITEM = MAX_UVALUE(size_t); ///< Contant to indicate we can't allocate any more items
|
||||
|
||||
/**
|
||||
* Makes given index valid
|
||||
* @param size size of item
|
||||
* @param index index of item
|
||||
* @pre index < this->size
|
||||
* @pre this->Get(index) == NULL
|
||||
*/
|
||||
void *AllocateItem(size_t size, size_t index);
|
||||
|
||||
/**
|
||||
* Resizes the pool so 'index' can be addressed
|
||||
* @param index index we will allocate later
|
||||
* @pre index >= this->size
|
||||
* @pre index < Tmax_size
|
||||
*/
|
||||
void ResizeFor(size_t index);
|
||||
|
||||
/**
|
||||
* Searches for first free index
|
||||
* @return first free index, NO_FREE_ITEM on failure
|
||||
*/
|
||||
size_t FindFirstFree();
|
||||
|
||||
/**
|
||||
* Allocates new item
|
||||
* @param size size of item
|
||||
* @return pointer to allocated item
|
||||
* @note error() on failure! (no free item)
|
||||
*/
|
||||
void *GetNew(size_t size);
|
||||
|
||||
/**
|
||||
* Allocates new item with given index
|
||||
* @param size size of item
|
||||
* @param index index of item
|
||||
* @return pointer to allocated item
|
||||
* @note usererror() on failure! (index out of range or already used)
|
||||
*/
|
||||
void *GetNew(size_t size, size_t index);
|
||||
|
||||
/**
|
||||
* Deallocates memory used by this index and marks item as free
|
||||
* @param index item to deallocate
|
||||
* @pre unit is allocated (non-NULL)
|
||||
* @note 'delete NULL' doesn't cause call of this function, so it is safe
|
||||
*/
|
||||
void FreeItem(size_t index);
|
||||
};
|
||||
|
||||
#define FOR_ALL_ITEMS_FROM(type, iter, var, start) \
|
||||
for (size_t iter = start; var = NULL, iter < type::GetPoolSize(); iter++) \
|
||||
if ((var = type::Get(iter)) != NULL)
|
||||
|
||||
#define FOR_ALL_ITEMS(type, iter, var) FOR_ALL_ITEMS_FROM(type, iter, var, 0)
|
||||
|
||||
#endif /* POOL_HPP */
|
@ -0,0 +1,139 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file pool_func.hpp Some methods of Pool are placed here in order to reduce compilation time and binary size. */
|
||||
|
||||
#ifndef POOL_FUNC_HPP
|
||||
#define POOL_FUNC_HPP
|
||||
|
||||
#include "alloc_func.hpp"
|
||||
#include "mem_func.hpp"
|
||||
#include "pool.hpp"
|
||||
|
||||
#define DEFINE_POOL_METHOD(type) \
|
||||
template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size> \
|
||||
type Pool<Titem, Tindex, Tgrowth_step, Tmax_size>
|
||||
|
||||
DEFINE_POOL_METHOD(inline)::Pool(const char *name) :
|
||||
name(name),
|
||||
size(0),
|
||||
first_free(0),
|
||||
first_unused(0),
|
||||
items(0),
|
||||
cleaning(false),
|
||||
data(NULL)
|
||||
{ }
|
||||
|
||||
DEFINE_POOL_METHOD(inline void)::ResizeFor(size_t index)
|
||||
{
|
||||
assert(index >= this->size);
|
||||
assert(index < Tmax_size);
|
||||
|
||||
size_t new_size = min(Tmax_size, Align(index + 1, Tgrowth_step));
|
||||
|
||||
this->data = ReallocT(this->data, new_size);
|
||||
MemSetT(this->data + this->size, 0, new_size - this->size);
|
||||
|
||||
this->size = new_size;
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(inline size_t)::FindFirstFree()
|
||||
{
|
||||
size_t index = this->first_free;
|
||||
|
||||
for (; index < this->first_unused; index++) {
|
||||
if (this->data[index] == NULL) return index;
|
||||
}
|
||||
|
||||
if (index < this->size) {
|
||||
return index;
|
||||
}
|
||||
|
||||
assert(index == this->size);
|
||||
assert(this->first_unused == this->size);
|
||||
|
||||
if (index < Tmax_size) {
|
||||
this->ResizeFor(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
assert(this->items == Tmax_size);
|
||||
|
||||
return NO_FREE_ITEM;
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(inline void *)::AllocateItem(size_t size, size_t index)
|
||||
{
|
||||
assert(this->data[index] == NULL);
|
||||
|
||||
this->first_unused = max(this->first_unused, index + 1);
|
||||
this->items++;
|
||||
|
||||
Titem *item = this->data[index] = (Titem *)CallocT<byte>(size);
|
||||
item->index = (uint)index;
|
||||
return item;
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(void *)::GetNew(size_t size)
|
||||
{
|
||||
size_t index = this->FindFirstFree();
|
||||
|
||||
if (index == NO_FREE_ITEM) {
|
||||
error("%s: no more free items", this->name);
|
||||
}
|
||||
|
||||
this->first_free = index + 1;
|
||||
return this->AllocateItem(size, index);
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(void *)::GetNew(size_t size, size_t index)
|
||||
{
|
||||
if (index >= Tmax_size) {
|
||||
usererror("failed loading savegame: %s index " PRINTF_SIZE " out of range (" PRINTF_SIZE ")", this->name, index, Tmax_size);
|
||||
}
|
||||
|
||||
if (index >= this->size) this->ResizeFor(index);
|
||||
|
||||
if (this->data[index] != NULL) {
|
||||
usererror("failed loading savegame: %s index " PRINTF_SIZE " already in use", this->name, index);
|
||||
}
|
||||
|
||||
return this->AllocateItem(size, index);
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(void)::FreeItem(size_t index)
|
||||
{
|
||||
assert(index < this->size);
|
||||
assert(this->data[index] != NULL);
|
||||
free(this->data[index]);
|
||||
this->data[index] = NULL;
|
||||
this->first_free = min(this->first_free, index);
|
||||
this->items--;
|
||||
}
|
||||
|
||||
DEFINE_POOL_METHOD(void)::CleanPool()
|
||||
{
|
||||
this->cleaning = true;
|
||||
for (size_t i = 0; i < this->first_unused; i++) {
|
||||
delete this->Get(i); // 'delete NULL;' is very valid
|
||||
}
|
||||
assert(this->items == 0);
|
||||
free(this->data);
|
||||
this->first_unused = this->first_free = this->size = 0;
|
||||
this->data = NULL;
|
||||
this->cleaning = false;
|
||||
}
|
||||
|
||||
#undef DEFINE_POOL_METHOD
|
||||
|
||||
/**
|
||||
* Force instantiation of pool methods so we don't get linker errors.
|
||||
* Only methods accessed from methods defined in pool.hpp need to be
|
||||
* forcefully instantiated.
|
||||
*/
|
||||
#define INSTANTIATE_POOL_METHODS(name) \
|
||||
template void * name ## Pool::GetNew(size_t size); \
|
||||
template void * name ## Pool::GetNew(size_t size, size_t index); \
|
||||
template void name ## Pool::FreeItem(size_t index); \
|
||||
template void name ## Pool::CleanPool();
|
||||
|
||||
#endif /* POOL_FUNC_HPP */
|
@ -1,81 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file oldpool.cpp Implementation of the old pool. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "debug.h"
|
||||
#include "oldpool.h"
|
||||
#include "core/alloc_func.hpp"
|
||||
|
||||
/**
|
||||
* Clean a pool in a safe way (does free all blocks)
|
||||
*/
|
||||
void OldMemoryPoolBase::CleanPool()
|
||||
{
|
||||
uint i;
|
||||
|
||||
DEBUG(misc, 4, "[Pool] (%s) cleaning pool..", this->name);
|
||||
|
||||
this->cleaning_pool = true;
|
||||
/* Free all blocks */
|
||||
for (i = 0; i < this->current_blocks; i++) {
|
||||
if (this->clean_block_proc != NULL) {
|
||||
this->clean_block_proc(i * (1 << this->block_size_bits), (i + 1) * (1 << this->block_size_bits) - 1);
|
||||
}
|
||||
free(this->blocks[i]);
|
||||
}
|
||||
this->cleaning_pool = false;
|
||||
|
||||
/* Free the block itself */
|
||||
free(this->blocks);
|
||||
|
||||
/* Clear up some critical data */
|
||||
this->total_items = 0;
|
||||
this->current_blocks = 0;
|
||||
this->blocks = NULL;
|
||||
this->first_free_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function tries to increase the size of array by adding
|
||||
* 1 block too it
|
||||
*
|
||||
* @return Returns false if the pool could not be increased
|
||||
*/
|
||||
bool OldMemoryPoolBase::AddBlockToPool()
|
||||
{
|
||||
/* Is the pool at his max? */
|
||||
if (this->max_blocks == this->current_blocks) return false;
|
||||
|
||||
this->total_items = (this->current_blocks + 1) * (1 << this->block_size_bits);
|
||||
|
||||
DEBUG(misc, 4, "[Pool] (%s) increasing size of pool to %d items (%d bytes)", this->name, this->total_items, this->total_items * this->item_size);
|
||||
|
||||
/* Increase the poolsize */
|
||||
this->blocks = ReallocT(this->blocks, this->current_blocks + 1);
|
||||
|
||||
/* Allocate memory to the new block item */
|
||||
this->blocks[this->current_blocks] = CallocT<byte>(this->item_size * (1 << this->block_size_bits));
|
||||
|
||||
/* Call a custom function if defined (e.g. to fill indexes) */
|
||||
if (this->new_block_proc != NULL) this->new_block_proc(this->current_blocks * (1 << this->block_size_bits));
|
||||
|
||||
/* We have a new block */
|
||||
this->current_blocks++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds blocks to the pool if needed (and possible) till index fits inside the pool
|
||||
*
|
||||
* @return Returns false if adding failed
|
||||
*/
|
||||
bool OldMemoryPoolBase::AddBlockIfNeeded(uint index)
|
||||
{
|
||||
while (index >= this->total_items) {
|
||||
if (!this->AddBlockToPool()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,385 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file oldpool.h Base for the old pool. */
|
||||
|
||||
#ifndef OLDPOOL_H
|
||||
#define OLDPOOL_H
|
||||
|
||||
#include "core/math_func.hpp"
|
||||
|
||||
/* The function that is called after a new block is added
|
||||
start_item is the first item of the new made block */
|
||||
typedef void OldMemoryPoolNewBlock(uint start_item);
|
||||
/* The function that is called before a block is cleaned up */
|
||||
typedef void OldMemoryPoolCleanBlock(uint start_item, uint end_item);
|
||||
|
||||
/**
|
||||
* Stuff for dynamic vehicles. Use the wrappers to access the OldMemoryPool
|
||||
* please try to avoid manual calls!
|
||||
*/
|
||||
struct OldMemoryPoolBase {
|
||||
void CleanPool();
|
||||
bool AddBlockToPool();
|
||||
bool AddBlockIfNeeded(uint index);
|
||||
|
||||
protected:
|
||||
OldMemoryPoolBase(const char *name, uint max_blocks, uint block_size_bits, uint item_size,
|
||||
OldMemoryPoolNewBlock *new_block_proc, OldMemoryPoolCleanBlock *clean_block_proc) :
|
||||
name(name), max_blocks(max_blocks), block_size_bits(block_size_bits),
|
||||
new_block_proc(new_block_proc), clean_block_proc(clean_block_proc), current_blocks(0),
|
||||
total_items(0), cleaning_pool(false), item_size(item_size), first_free_index(0), blocks(NULL) {}
|
||||
|
||||
const char *name; ///< Name of the pool (just for debugging)
|
||||
|
||||
const uint max_blocks; ///< The max amount of blocks this pool can have
|
||||
const uint block_size_bits; ///< The size of each block in bits
|
||||
|
||||
/** Pointer to a function that is called after a new block is added */
|
||||
OldMemoryPoolNewBlock *new_block_proc;
|
||||
/** Pointer to a function that is called to clean a block */
|
||||
OldMemoryPoolCleanBlock *clean_block_proc;
|
||||
|
||||
uint current_blocks; ///< How many blocks we have in our pool
|
||||
uint total_items; ///< How many items we now have in this pool
|
||||
|
||||
bool cleaning_pool; ///< Are we currently cleaning the pool?
|
||||
public:
|
||||
const uint item_size; ///< How many bytes one block is
|
||||
uint first_free_index; ///< The index of the first free pool item in this pool
|
||||
byte **blocks; ///< An array of blocks (one block hold all the items)
|
||||
|
||||
/**
|
||||
* Check if the index of pool item being deleted is lower than cached first_free_index
|
||||
* @param index index of pool item
|
||||
* @note usage of min() will result in better code on some architectures
|
||||
*/
|
||||
inline void UpdateFirstFreeIndex(uint index)
|
||||
{
|
||||
first_free_index = min(first_free_index, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of this pool, i.e. the total number of items you
|
||||
* can put into it at the current moment; the pool might still
|
||||
* be able to increase the size of the pool.
|
||||
* @return the size of the pool
|
||||
*/
|
||||
inline uint GetSize() const
|
||||
{
|
||||
return this->total_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this pool allocate more blocks, i.e. is the maximum amount
|
||||
* of allocated blocks not yet reached?
|
||||
* @return the if and only if the amount of allocable blocks is
|
||||
* less than the amount of allocated blocks.
|
||||
*/
|
||||
inline bool CanAllocateMoreBlocks() const
|
||||
{
|
||||
return this->current_blocks < this->max_blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of allocable blocks.
|
||||
* @return the numebr of blocks
|
||||
*/
|
||||
inline uint GetBlockCount() const
|
||||
{
|
||||
return this->current_blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this pool.
|
||||
* @return the name
|
||||
*/
|
||||
inline const char *GetName() const
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the pool in the cleaning phase?
|
||||
* @return true if it is
|
||||
*/
|
||||
inline bool CleaningPool() const
|
||||
{
|
||||
return this->cleaning_pool;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct OldMemoryPool : public OldMemoryPoolBase {
|
||||
OldMemoryPool(const char *name, uint max_blocks, uint block_size_bits, uint item_size,
|
||||
OldMemoryPoolNewBlock *new_block_proc, OldMemoryPoolCleanBlock *clean_block_proc) :
|
||||
OldMemoryPoolBase(name, max_blocks, block_size_bits, item_size, new_block_proc, clean_block_proc) {}
|
||||
|
||||
/**
|
||||
* Get the pool entry at the given index.
|
||||
* @param index the index into the pool
|
||||
* @pre index < this->GetSize()
|
||||
* @return the pool entry.
|
||||
*/
|
||||
inline T *Get(uint index) const
|
||||
{
|
||||
assert(index < this->GetSize());
|
||||
return (T*)(this->blocks[index >> this->block_size_bits] +
|
||||
(index & ((1 << this->block_size_bits) - 1)) * this->item_size);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic function to initialize a new block in a pool.
|
||||
* @param start_item the first item that needs to be initialized
|
||||
*/
|
||||
template <typename T, OldMemoryPool<T> *Tpool>
|
||||
static void PoolNewBlock(uint start_item)
|
||||
{
|
||||
for (T *t = Tpool->Get(start_item); t != NULL; t = (t->index + 1U < Tpool->GetSize()) ? Tpool->Get(t->index + 1U) : NULL) {
|
||||
t = new (t) T();
|
||||
t->index = start_item++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic function to free a new block in a pool.
|
||||
* @param start_item the first item that needs to be cleaned
|
||||
* @param end_item the last item that needs to be cleaned
|
||||
*/
|
||||
template <typename T, OldMemoryPool<T> *Tpool>
|
||||
static void PoolCleanBlock(uint start_item, uint end_item)
|
||||
{
|
||||
for (uint i = start_item; i <= end_item; i++) {
|
||||
T *t = Tpool->Get(i);
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template providing a predicate to allow STL containers of
|
||||
* pointers to pool items to be sorted by index.
|
||||
*/
|
||||
template <typename T>
|
||||
struct PoolItemIndexLess {
|
||||
/**
|
||||
* The actual comparator.
|
||||
* @param lhs the left hand side of the comparison.
|
||||
* @param rhs the right hand side of the comparison.
|
||||
* @return true if lhs' index is less than rhs' index.
|
||||
*/
|
||||
bool operator()(const T *lhs, const T *rhs) const
|
||||
{
|
||||
return lhs->index < rhs->index;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generalization for all pool items that are saved in the savegame.
|
||||
* It specifies all the mechanics to access the pool easily.
|
||||
*/
|
||||
template <typename T, typename Tid, OldMemoryPool<T> *Tpool>
|
||||
struct PoolItem {
|
||||
/**
|
||||
* The pool-wide index of this object.
|
||||
*/
|
||||
Tid index;
|
||||
|
||||
/**
|
||||
* We like to have the correct class destructed.
|
||||
* @warning It is called even for object allocated on stack,
|
||||
* so it is not present in the TPool!
|
||||
* Then, index is undefined, not associated with TPool in any way.
|
||||
* @note The idea is to free up allocated memory etc.
|
||||
*/
|
||||
virtual ~PoolItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor of given class.
|
||||
* @warning It is called even for object allocated on stack,
|
||||
* so it may not be present in TPool!
|
||||
* Then, index is undefined, not associated with TPool in any way.
|
||||
* @note The idea is to initialize variables (except index)
|
||||
*/
|
||||
PoolItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An overriden version of new that allocates memory on the pool.
|
||||
* @param size the size of the variable (unused)
|
||||
* @pre CanAllocateItem()
|
||||
* @return the memory that is 'allocated'
|
||||
*/
|
||||
void *operator new(size_t size)
|
||||
{
|
||||
return AllocateRaw();
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Free' the memory allocated by the overriden new.
|
||||
* @param p the memory to 'free'
|
||||
* @note we only update Tpool->first_free_index
|
||||
*/
|
||||
void operator delete(void *p)
|
||||
{
|
||||
Tpool->UpdateFirstFreeIndex(((T*)p)->index);
|
||||
}
|
||||
|
||||
/**
|
||||
* An overriden version of new, so you can directly allocate a new object with
|
||||
* the correct index when one is loading the savegame.
|
||||
* @param size the size of the variable (unused)
|
||||
* @param index the index of the object
|
||||
* @return the memory that is 'allocated'
|
||||
*/
|
||||
void *operator new(size_t size, int index)
|
||||
{
|
||||
if (!Tpool->AddBlockIfNeeded(index)) error("%s: failed loading savegame: too many %s", Tpool->GetName(), Tpool->GetName());
|
||||
|
||||
return Tpool->Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Free' the memory allocated by the overriden new.
|
||||
* @param p the memory to 'free'
|
||||
* @param index the original parameter given to create the memory
|
||||
* @note we only update Tpool->first_free_index
|
||||
*/
|
||||
void operator delete(void *p, int index)
|
||||
{
|
||||
Tpool->UpdateFirstFreeIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* An overriden version of new, so you can use the vehicle instance
|
||||
* instead of a newly allocated piece of memory.
|
||||
* @param size the size of the variable (unused)
|
||||
* @param pn the already existing object to use as 'storage' backend
|
||||
* @return the memory that is 'allocated'
|
||||
*/
|
||||
void *operator new(size_t size, T *pn)
|
||||
{
|
||||
return pn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Free' the memory allocated by the overriden new.
|
||||
* @param p the memory to 'free'
|
||||
* @param pn the pointer that was given to 'new' on creation.
|
||||
* @note we only update Tpool->first_free_index
|
||||
*/
|
||||
void operator delete(void *p, T *pn)
|
||||
{
|
||||
Tpool->UpdateFirstFreeIndex(pn->index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item with given index
|
||||
* @param index item to get
|
||||
*/
|
||||
static FORCEINLINE T *Get(uint index)
|
||||
{
|
||||
return Tpool->Get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item with given index
|
||||
* @param index item to get
|
||||
* @return NULL for invalid items
|
||||
*/
|
||||
static FORCEINLINE T *GetIfValid(uint index)
|
||||
{
|
||||
if (index >= Tpool->GetSize()) return NULL;
|
||||
T *item = Tpool->Get(index);
|
||||
return item->IsValid() ? item : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns size of the pool (in number of items)
|
||||
* @return size of the pool
|
||||
*/
|
||||
static FORCEINLINE uint GetPoolSize()
|
||||
{
|
||||
return Tpool->GetSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if given ID belongs to valid pool item
|
||||
* @return is given ID valid?
|
||||
*/
|
||||
static FORCEINLINE bool IsValidID(uint index)
|
||||
{
|
||||
return index < Tpool->GetSize() && Tpool->Get(index)->IsValid();
|
||||
}
|
||||
|
||||
private:
|
||||
static T *AllocateSafeRaw(uint &first);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Allocate a pool item; possibly allocate a new block in the pool.
|
||||
* @pre CanAllocateItem()
|
||||
* @return the allocated pool item.
|
||||
*/
|
||||
static inline T *AllocateRaw()
|
||||
{
|
||||
return AllocateSafeRaw(Tpool->first_free_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a pool item; possibly allocate a new block in the pool.
|
||||
* @param first the first pool item to start searching
|
||||
* @pre CanAllocateItem()
|
||||
* @return the allocated pool item.
|
||||
*/
|
||||
static inline T *AllocateRaw(uint &first)
|
||||
{
|
||||
if (first >= Tpool->GetSize() && !Tpool->AddBlockToPool()) return NULL;
|
||||
|
||||
return AllocateSafeRaw(first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we cleaning this pool?
|
||||
* @return true if we are
|
||||
*/
|
||||
static inline bool CleaningPool()
|
||||
{
|
||||
return Tpool->CleaningPool();
|
||||
}
|
||||
|
||||
public:
|
||||
static bool CanAllocateItem(uint count = 1);
|
||||
};
|
||||
|
||||
|
||||
#define OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \
|
||||
enum { \
|
||||
name##_POOL_BLOCK_SIZE_BITS = block_size_bits, \
|
||||
name##_POOL_MAX_BLOCKS = max_blocks \
|
||||
};
|
||||
|
||||
|
||||
#define DECLARE_OLD_POOL(name, type, block_size_bits, max_blocks) \
|
||||
OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \
|
||||
extern OldMemoryPool<type> _##name##_pool;
|
||||
|
||||
|
||||
#define DEFINE_OLD_POOL_GENERIC(name, type) \
|
||||
OldMemoryPool<type> _##name##_pool( \
|
||||
#name, name##_POOL_MAX_BLOCKS, name##_POOL_BLOCK_SIZE_BITS, sizeof(type), \
|
||||
PoolNewBlock<type, &_##name##_pool>, PoolCleanBlock<type, &_##name##_pool>); \
|
||||
template type *PoolItem<type, type##ID, &_##name##_pool>::AllocateSafeRaw(uint &first); \
|
||||
template bool PoolItem<type, type##ID, &_##name##_pool>::CanAllocateItem(uint count);
|
||||
|
||||
#define FOR_ALL_ITEMS_FROM(type, iter, var, start) \
|
||||
for (size_t iter = start; var = NULL, iter < type::GetPoolSize(); iter++) \
|
||||
if ((var = type::Get(iter))->IsValid())
|
||||
|
||||
#define FOR_ALL_ITEMS(type, iter, var) FOR_ALL_ITEMS_FROM(type, iter, var, 0)
|
||||
|
||||
#endif /* OLDPOOL_H */
|
@ -1,63 +0,0 @@
|
||||
/* $Id$ */
|
||||
|
||||
/** @file oldpool_func.h Functions related to the old pool. */
|
||||
|
||||
#ifndef OLDPOOL_FUNC_H
|
||||
#define OLDPOOL_FUNC_H
|
||||
|
||||
#include "oldpool.h"
|
||||
|
||||
/**
|
||||
* Allocate a pool item; possibly allocate a new block in the pool.
|
||||
* @param first the first pool item to start searching
|
||||
* @pre first <= Tpool->GetSize()
|
||||
* @pre CanAllocateItem()
|
||||
* @return the allocated pool item
|
||||
*/
|
||||
template<typename T, typename Tid, OldMemoryPool<T> *Tpool> T *PoolItem<T, Tid, Tpool>::AllocateSafeRaw(uint &first)
|
||||
{
|
||||
uint last_minus_one = Tpool->GetSize() - 1;
|
||||
|
||||
for (T *t = Tpool->Get(first); t != NULL; t = ((uint)t->index < last_minus_one) ? Tpool->Get(t->index + 1U) : NULL) {
|
||||
if (!t->IsValid()) {
|
||||
first = t->index;
|
||||
Tid index = t->index;
|
||||
|
||||
memset(t, 0, Tpool->item_size);
|
||||
t->index = index;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we can add a block to the pool */
|
||||
if (Tpool->AddBlockToPool()) return AllocateRaw(first);
|
||||
|
||||
/* One should *ALWAYS* be sure to have enough space before making vehicles! */
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we can allocate an item in this pool. This to prevent the
|
||||
* need to actually construct the object and then destructing it again,
|
||||
* which could be *very* costly.
|
||||
* @param count the number of items to create
|
||||
* @return true if and only if at least count items can be allocated.
|
||||
*/
|
||||
template<typename T, typename Tid, OldMemoryPool<T> *Tpool> bool PoolItem<T, Tid, Tpool>::CanAllocateItem(uint count)
|
||||
{
|
||||
uint last_minus_one = Tpool->GetSize() - 1;
|
||||
uint orig_count = count;
|
||||
|
||||
for (T *t = Tpool->Get(Tpool->first_free_index); count > 0 && t != NULL; t = ((uint)t->index < last_minus_one) ? Tpool->Get(t->index + 1U) : NULL) {
|
||||
if (!t->IsValid()) count--;
|
||||
}
|
||||
|
||||
if (count == 0) return true;
|
||||
|
||||
/* Check if we can add a block to the pool */
|
||||
if (Tpool->AddBlockToPool()) return CanAllocateItem(orig_count);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* OLDPOOL_FUNC_H */
|
Loading…
Reference in New Issue