diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/pool.hpp | 284 | ||||
-rw-r--r-- | src/core/pool_func.hpp | 139 | ||||
-rw-r--r-- | src/core/random_func.cpp | 2 |
3 files changed, 424 insertions, 1 deletions
diff --git a/src/core/pool.hpp b/src/core/pool.hpp new file mode 100644 index 000000000..7c95803f7 --- /dev/null +++ b/src/core/pool.hpp @@ -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 */ diff --git a/src/core/pool_func.hpp b/src/core/pool_func.hpp new file mode 100644 index 000000000..df9e2692f --- /dev/null +++ b/src/core/pool_func.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 */ diff --git a/src/core/random_func.cpp b/src/core/random_func.cpp index 6e9cb0a94..4e2897625 100644 --- a/src/core/random_func.cpp +++ b/src/core/random_func.cpp @@ -40,7 +40,7 @@ void SetRandomSeed(uint32 seed) uint32 DoRandom(int line, const char *file) { - if (_networking && (NetworkClientSocket::Get(0)->status != STATUS_INACTIVE || !_network_server)) { + if (_networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != STATUS_INACTIVE))) { printf("Random [%d/%d] %s:%d\n", _frame_counter, (byte)_current_company, file, line); } |