summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/pool.hpp284
-rw-r--r--src/core/pool_func.hpp139
-rw-r--r--src/core/random_func.cpp2
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);
}