OpenTTD/src/oldpool.h

358 lines
10 KiB
C++

/* $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 */