/* $Id$ */ /** @file oldpool.h */ #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; } } /** * 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) * @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); } private: /** * 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() * @return the allocated pool item (or NULL when the pool is full). */ static inline T *AllocateSafeRaw(uint &first) { uint last_minus_one = Tpool->GetSize() - 1; for (T *t = Tpool->Get(first); t != NULL; t = (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); return NULL; } protected: /** * Allocate a pool item; possibly allocate a new block in the pool. * @return the allocated pool item (or NULL when the pool is full). */ 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 * @return the allocated pool item (or NULL when the pool is full). */ 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(); } }; #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 OLD_POOL_ACCESSORS(name, type) \ static inline type* Get##name(uint index) { return _##name##_pool.Get(index); } \ static inline uint Get##name##PoolSize() { return _##name##_pool.GetSize(); } #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; \ OLD_POOL_ACCESSORS(name, type) #define DEFINE_OLD_POOL(name, type, new_block_proc, clean_block_proc) \ OldMemoryPool<type> _##name##_pool( \ #name, name##_POOL_MAX_BLOCKS, name##_POOL_BLOCK_SIZE_BITS, sizeof(type), \ new_block_proc, clean_block_proc); #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>); #define STATIC_OLD_POOL(name, type, block_size_bits, max_blocks, new_block_proc, clean_block_proc) \ OLD_POOL_ENUM(name, type, block_size_bits, max_blocks) \ static DEFINE_OLD_POOL(name, type, new_block_proc, clean_block_proc) \ OLD_POOL_ACCESSORS(name, type) #endif /* OLDPOOL_H */