Преглед на файлове

Script Engine Fixes
* Cleaned up db_vdestroy errors at server shutdown.
* Expanded the ERS system. Thanks to Hercules.
* Added ERS to the database trees to speed up iterator memory allocation. Thanks to Hercules.

aleos89 преди 9 години
родител
ревизия
f53916e060
променени са 7 файла, в които са добавени 420 реда и са изтрити 321 реда
  1. 101 84
      src/common/db.c
  2. 42 40
      src/common/db.h
  3. 144 64
      src/common/ers.c
  4. 24 18
      src/common/ers.h
  5. 1 2
      src/map/npc.c
  6. 99 111
      src/map/script.c
  7. 9 2
      src/map/script.h

+ 101 - 84
src/common/db.c

@@ -48,6 +48,7 @@
  *
  *  HISTORY:
  *    2013/08/25 - Added int64/uint64 support for keys [Ind/Hercules]
+ *    2013/04/27 - Added ERS to speed up iterator memory allocation [Ind/Hercules]
  *    2012/03/09 - Added enum for data types (int, uint, void*)
  *    2008/02/19 - Fixed db_obj_get not handling deleted entries correctly.
  *    2007/11/09 - Added an iterator to the database.
@@ -66,15 +67,18 @@
  * @encoding US-ASCII
  * @see #db.h
 \*****************************************************************************/
-#include <stdio.h>
-#include <stdlib.h>
 
 #include "db.h"
+
+#include "../common/ers.h"
 #include "../common/malloc.h"
+#include "../common/mmo.h"
 #include "../common/showmsg.h"
-#include "../common/ers.h"
 #include "../common/strlib.h"
 
+#include <stdio.h>
+#include <stdlib.h>
+
 /*****************************************************************************\
  *  (1) Private typedefs, enums, structures, defines and global variables of *
  *  the database system.                                                     *
@@ -139,7 +143,7 @@ typedef struct dbn {
 	// Other
 	node_color color;
 	unsigned deleted : 1;
-} *DBNode;
+} DBNode;
 
 /**
  * Structure that holds a deleted node.
@@ -149,8 +153,8 @@ typedef struct dbn {
  * @see DBMap_impl#free_list
  */
 struct db_free {
-	DBNode node;
-	DBNode *root;
+	DBNode *node;
+	DBNode **root;
 };
 
 /**
@@ -187,12 +191,12 @@ typedef struct DBMap_impl {
 	unsigned int free_max;
 	unsigned int free_lock;
 	// Other
-	ERS nodes;
+	ERS *nodes;
 	DBComparator cmp;
 	DBHasher hash;
 	DBReleaser release;
-	DBNode ht[HASH_SIZE];
-	DBNode cache;
+	DBNode *ht[HASH_SIZE];
+	DBNode *cache;
 	DBType type;
 	DBOptions options;
 	uint32 item_count;
@@ -216,7 +220,7 @@ typedef struct DBIterator_impl {
 	struct DBIterator vtable;
 	DBMap_impl* db;
 	int ht_index;
-	DBNode node;
+	DBNode *node;
 } DBIterator_impl;
 
 #if defined(DB_ENABLE_STATS)
@@ -324,11 +328,15 @@ static struct db_stats {
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0
 };
-#define DB_COUNTSTAT(token) if (stats. ## token != UINT32_MAX) ++stats. ## token
+#define DB_COUNTSTAT(token) do { if ((stats.token) != UINT32_MAX) ++(stats.token); } while(0)
 #else /* !defined(DB_ENABLE_STATS) */
 #define DB_COUNTSTAT(token)
 #endif /* !defined(DB_ENABLE_STATS) */
 
+/* [Ind/Hercules] */
+struct eri *db_iterator_ers;
+struct eri *db_alloc_ers;
+
 /*****************************************************************************\
  *  (2) Section of private functions used by the database system.            *
  *  db_rotate_left     - Rotate a tree node to the left.                     *
@@ -351,12 +359,12 @@ static struct db_stats {
  * @param node Node to be rotated
  * @param root Pointer to the root of the tree
  * @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
+ * @see #db_rebalance(DBNode *,DBNode **)
+ * @see #db_rebalance_erase(DBNode *,DBNode **)
  */
-static void db_rotate_left(DBNode node, DBNode *root)
+static void db_rotate_left(DBNode *node, DBNode **root)
 {
-	DBNode y = node->right;
+	DBNode *y = node->right;
 
 	DB_COUNTSTAT(db_rotate_left);
 	// put the left of y at the right of node
@@ -382,12 +390,12 @@ static void db_rotate_left(DBNode node, DBNode *root)
  * @param node Node to be rotated
  * @param root Pointer to the root of the tree
  * @private
- * @see #db_rebalance(DBNode,DBNode *)
- * @see #db_rebalance_erase(DBNode,DBNode *)
+ * @see #db_rebalance(DBNode *,DBNode **)
+ * @see #db_rebalance_erase(DBNode *,DBNode **)
  */
-static void db_rotate_right(DBNode node, DBNode *root)
+static void db_rotate_right(DBNode *node, DBNode **root)
 {
-	DBNode y = node->left;
+	DBNode *y = node->left;
 
 	DB_COUNTSTAT(db_rotate_right);
 	// put the right of y at the left of node
@@ -414,13 +422,13 @@ static void db_rotate_right(DBNode node, DBNode *root)
  * @param node Node to be rebalanced
  * @param root Pointer to the root of the tree
  * @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_rotate_left(DBNode *,DBNode **)
+ * @see #db_rotate_right(DBNode *,DBNode **)
  * @see #db_obj_put(DBMap*,DBKey,DBData)
  */
-static void db_rebalance(DBNode node, DBNode *root)
+static void db_rebalance(DBNode *node, DBNode **root)
 {
-	DBNode y;
+	DBNode *y;
 
 	DB_COUNTSTAT(db_rebalance);
 	// Restore the RED-BLACK properties
@@ -476,15 +484,15 @@ static void db_rebalance(DBNode node, DBNode *root)
  * @param node Node to be erased from the tree
  * @param root Root of the tree
  * @private
- * @see #db_rotate_left(DBNode,DBNode *)
- * @see #db_rotate_right(DBNode,DBNode *)
+ * @see #db_rotate_left(DBNode *,DBNode **)
+ * @see #db_rotate_right(DBNode *,DBNode **)
  * @see #db_free_unlock(DBMap_impl*)
  */
-static void db_rebalance_erase(DBNode node, DBNode *root)
+static void db_rebalance_erase(DBNode *node, DBNode **root)
 {
-	DBNode y = node;
-	DBNode x = NULL;
-	DBNode x_parent = NULL;
+	DBNode *y = node;
+	DBNode *x = NULL;
+	DBNode *x_parent = NULL;
 
 	DB_COUNTSTAT(db_rebalance_erase);
 	// Select where to change the tree
@@ -552,7 +560,7 @@ static void db_rebalance_erase(DBNode node, DBNode *root)
 	// Restore the RED-BLACK properties
 	if (y->color != RED) {
 		while (x != *root && (x == NULL || x->color == BLACK)) {
-			DBNode w;
+			DBNode *w;
 			if (x == x_parent->left) {
 				w = x_parent->right;
 				if (w->color == RED) {
@@ -640,8 +648,8 @@ static int db_is_key_null(DBType type, DBKey key)
  * @param key Key to be duplicated
  * @param Duplicated key
  * @private
- * @see #db_free_add(DBMap_impl*,DBNode,DBNode *)
- * @see #db_free_remove(DBMap_impl*,DBNode)
+ * @see #db_free_add(DBMap_impl*,DBNode *,DBNode **)
+ * @see #db_free_remove(DBMap_impl*,DBNode *)
  * @see #db_obj_put(DBMap*,DBKey,void *)
  * @see #db_dup_key_free(DBMap_impl*,DBKey)
  */
@@ -700,9 +708,9 @@ static void db_dup_key_free(DBMap_impl* db, DBKey key)
  * @see DBMap_impl#free_count
  * @see DBMap_impl#free_max
  * @see #db_obj_remove(DBMap*,DBKey)
- * @see #db_free_remove(DBMap_impl*,DBNode)
+ * @see #db_free_remove(DBMap_impl*,DBNode *)
  */
-static void db_free_add(DBMap_impl* db, DBNode node, DBNode *root)
+static void db_free_add(DBMap_impl* db, DBNode *node, DBNode **root)
 {
 	DBKey old_key;
 
@@ -749,9 +757,9 @@ static void db_free_add(DBMap_impl* db, DBNode node, DBNode *root)
  * @see DBMap_impl#free_list
  * @see DBMap_impl#free_count
  * @see #db_obj_put(DBMap*,DBKey,DBData)
- * @see #db_free_add(DBMap_impl*,DBNode*,DBNode)
+ * @see #db_free_add(DBMap_impl*,DBNode**,DBNode*)
  */
-static void db_free_remove(DBMap_impl* db, DBNode node)
+static void db_free_remove(DBMap_impl* db, DBNode *node)
 {
 	unsigned int i;
 
@@ -800,7 +808,7 @@ static void db_free_lock(DBMap_impl* db)
  * @param db Target database
  * @private
  * @see DBMap_impl#free_lock
- * @see #db_free_dbn(DBNode)
+ * @see #db_free_dbn(DBNode*)
  * @see #db_lock(DBMap_impl*)
  */
 static void db_free_unlock(DBMap_impl* db)
@@ -1145,7 +1153,10 @@ static void db_release_data(DBKey key, DBData data, DBRelease which)
 {
 	(void)key;//not used
 	DB_COUNTSTAT(db_release_data);
-	if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) aFree(data.u.ptr);
+	if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) {
+		aFree(data.u.ptr);
+		data.u.ptr = NULL;
+	}
 }
 
 /**
@@ -1164,7 +1175,10 @@ static void db_release_both(DBKey key, DBData data, DBRelease which)
 {
 	DB_COUNTSTAT(db_release_both);
 	if (which&DB_RELEASE_KEY) aFree((char*)key.str); // needs to be a pointer
-	if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) aFree(data.u.ptr);
+	if (which&DB_RELEASE_DATA && data.type == DB_DATA_PTR) {
+		aFree(data.u.ptr);
+		data.u.ptr = NULL;
+	}
 }
 
 /*****************************************************************************\
@@ -1257,8 +1271,8 @@ DBData* dbit_obj_last(DBIterator* self, DBKey* out_key)
 DBData* dbit_obj_next(DBIterator* self, DBKey* out_key)
 {
 	DBIterator_impl* it = (DBIterator_impl*)self;
-	DBNode node;
-	DBNode parent;
+	DBNode *node;
+	DBNode *parent;
 	struct dbn fake;
 
 	DB_COUNTSTAT(dbit_next);
@@ -1333,8 +1347,8 @@ DBData* dbit_obj_next(DBIterator* self, DBKey* out_key)
 DBData* dbit_obj_prev(DBIterator* self, DBKey* out_key)
 {
 	DBIterator_impl* it = (DBIterator_impl*)self;
-	DBNode node;
-	DBNode parent;
+	DBNode *node;
+	DBNode *parent;
 	struct dbn fake;
 
 	DB_COUNTSTAT(dbit_prev);
@@ -1417,7 +1431,7 @@ bool dbit_obj_exists(DBIterator* self)
  * Removes the current entry from the database.
  * NOTE: {@link DBIterator#exists} will return false until another entry
  *       is fetched
- * Puts data of the removed entry in out_data, if out_data is not NULL.
+ * Puts data of the removed entry in out_data, if out_data is not NULL (unless data has been released)
  * @param self Iterator
  * @param out_data Data of the removed entry.
  * @return 1 if entry was removed, 0 otherwise
@@ -1428,7 +1442,7 @@ bool dbit_obj_exists(DBIterator* self)
 int dbit_obj_remove(DBIterator* self, DBData *out_data)
 {
 	DBIterator_impl* it = (DBIterator_impl*)self;
-	DBNode node;
+	DBNode *node;
 	int retval = 0;
 
 	DB_COUNTSTAT(dbit_remove);
@@ -1438,10 +1452,10 @@ int dbit_obj_remove(DBIterator* self, DBData *out_data)
 		DBMap_impl* db = it->db;
 		if( db->cache == node )
 			db->cache = NULL;
+		db->release(node->key, node->data, DB_RELEASE_DATA);
 		if( out_data )
 			memcpy(out_data, &node->data, sizeof(DBData));
 		retval = 1;
-		db->release(node->key, node->data, DB_RELEASE_DATA);
 		db_free_add(db, node, &db->ht[it->ht_index]);
 	}
 	return retval;
@@ -1460,7 +1474,7 @@ void dbit_obj_destroy(DBIterator* self)
 	// unlock the database
 	db_free_unlock(it->db);
 	// free iterator
-	aFree(self);
+	ers_free(db_iterator_ers,self);
 }
 
 /**
@@ -1478,7 +1492,7 @@ static DBIterator* db_obj_iterator(DBMap* self)
 	DBIterator_impl* it;
 
 	DB_COUNTSTAT(db_iterator);
-	CREATE(it, struct DBIterator_impl, 1);
+	it = ers_alloc(db_iterator_ers, struct DBIterator_impl);
 	/* Interface of the iterator **/
 	it->vtable.first   = dbit_obj_first;
 	it->vtable.last    = dbit_obj_last;
@@ -1507,7 +1521,7 @@ static DBIterator* db_obj_iterator(DBMap* self)
 static bool db_obj_exists(DBMap* self, DBKey key)
 {
 	DBMap_impl* db = (DBMap_impl*)self;
-	DBNode node;
+	DBNode *node;
 	bool found = false;
 
 	DB_COUNTSTAT(db_exists);
@@ -1557,7 +1571,7 @@ static bool db_obj_exists(DBMap* self, DBKey key)
 static DBData* db_obj_get(DBMap* self, DBKey key)
 {
 	DBMap_impl* db = (DBMap_impl*)self;
-	DBNode node;
+	DBNode *node;
 	DBData *data = NULL;
 
 	DB_COUNTSTAT(db_get);
@@ -1617,8 +1631,8 @@ static unsigned int db_obj_vgetall(DBMap* self, DBData **buf, unsigned int max,
 {
 	DBMap_impl* db = (DBMap_impl*)self;
 	unsigned int i;
-	DBNode node;
-	DBNode parent;
+	DBNode *node;
+	DBNode *parent;
 	unsigned int ret = 0;
 
 	DB_COUNTSTAT(db_vgetall);
@@ -1714,8 +1728,8 @@ static unsigned int db_obj_getall(DBMap* self, DBData **buf, unsigned int max, D
 static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_list args)
 {
 	DBMap_impl* db = (DBMap_impl*)self;
-	DBNode node;
-	DBNode parent = NULL;
+	DBNode *node;
+	DBNode *parent = NULL;
 	unsigned int hash;
 	int c = 0;
 	DBData *data = NULL;
@@ -1783,7 +1797,7 @@ static DBData* db_obj_vensure(DBMap* self, DBKey key, DBCreateData create, va_li
 		if (db->options&DB_OPT_DUP_KEY) {
 			node->key = db_dup_key(db, key);
 			if (db->options&DB_OPT_RELEASE_KEY)
-				db->release(key, *data, DB_RELEASE_KEY);
+				db->release(key, node->data, DB_RELEASE_KEY);
 		} else {
 			node->key = key;
 		}
@@ -1827,7 +1841,7 @@ static DBData* db_obj_ensure(DBMap* self, DBKey key, DBCreateData create, ...)
 
 /**
  * Put the data identified by the key in the database.
- * Puts the previous data in out_data, if out_data is not NULL.
+ * Puts the previous data in out_data, if out_data is not NULL. (unless data has been released)
  * NOTE: Uses the new key, the old one is released.
  * @param self Interface of the database
  * @param key Key that identifies the data
@@ -1837,12 +1851,14 @@ static DBData* db_obj_ensure(DBMap* self, DBKey key, DBCreateData create, ...)
  * @protected
  * @see #db_malloc_dbn(void)
  * @see DBMap#put
+ * FIXME: If this method fails shouldn't it return another value?
+ *        Other functions rely on this to know if they were able to put something [Panikon]
  */
 static int db_obj_put(DBMap* self, DBKey key, DBData data, DBData *out_data)
 {
 	DBMap_impl* db = (DBMap_impl*)self;
-	DBNode node;
-	DBNode parent = NULL;
+	DBNode *node;
+	DBNode *parent = NULL;
 	int c = 0, retval = 0;
 	unsigned int hash;
 
@@ -1933,20 +1949,20 @@ static int db_obj_put(DBMap* self, DBKey key, DBData data, DBData *out_data)
 
 /**
  * Remove an entry from the database.
- * Puts the previous data in out_data, if out_data is not NULL.
- * NOTE: The key (of the database) is released in {@link #db_free_add(DBMap_impl*,DBNode,DBNode *)}.
+ * Puts the previous data in out_data, if out_data is not NULL. (unless data has been released)
+ * NOTE: The key (of the database) is released in {@link #db_free_add(DBMap_impl*,DBNode*,DBNode **)}.
  * @param self Interface of the database
  * @param key Key that identifies the entry
  * @param out_data Previous data if the entry exists
  * @return 1 if if the entry already exists, 0 otherwise
  * @protected
- * @see #db_free_add(DBMap_impl*,DBNode,DBNode *)
+ * @see #db_free_add(DBMap_impl*,DBNode*,DBNode **)
  * @see DBMap#remove
  */
 static int db_obj_remove(DBMap* self, DBKey key, DBData *out_data)
 {
 	DBMap_impl* db = (DBMap_impl*)self;
-	DBNode node;
+	DBNode *node;
 	unsigned int hash;
 	int retval = 0;
 
@@ -1971,10 +1987,10 @@ static int db_obj_remove(DBMap* self, DBKey key, DBData *out_data)
 			if (!(node->deleted)) {
 				if (db->cache == node)
 					db->cache = NULL;
+				db->release(node->key, node->data, DB_RELEASE_DATA);
 				if (out_data)
 					memcpy(out_data, &node->data, sizeof(*out_data));
 				retval = 1;
-				db->release(node->key, node->data, DB_RELEASE_DATA);
 				db_free_add(db, node, &db->ht[hash]);
 			}
 			break;
@@ -2003,8 +2019,8 @@ static int db_obj_vforeach(DBMap* self, DBApply func, va_list args)
 	DBMap_impl* db = (DBMap_impl*)self;
 	unsigned int i;
 	int sum = 0;
-	DBNode node;
-	DBNode parent;
+	DBNode *node;
+	DBNode *parent;
 
 	DB_COUNTSTAT(db_vforeach);
 	if (db == NULL) return 0; // nullpo candidate
@@ -2089,8 +2105,8 @@ static int db_obj_vclear(DBMap* self, DBApply func, va_list args)
 	DBMap_impl* db = (DBMap_impl*)self;
 	int sum = 0;
 	unsigned int i;
-	DBNode node;
-	DBNode parent;
+	DBNode *node;
+	DBNode *parent;
 
 	DB_COUNTSTAT(db_vclear);
 	if (db == NULL) return 0; // nullpo candidate
@@ -2221,7 +2237,7 @@ static int db_obj_vdestroy(DBMap* self, DBApply func, va_list args)
 	db->free_max = 0;
 	ers_destroy(db->nodes);
 	db_free_unlock(db);
-	aFree(db);
+	ers_free(db_alloc_ers, db);
 	return sum;
 }
 
@@ -2501,10 +2517,10 @@ DBReleaser db_custom_release(DBRelease which)
  * @see #DBMap_impl
  * @see #db_fix_options(DBType,DBOptions)
  */
-DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen)
-{
+DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOptions options, unsigned short maxlen) {
 	DBMap_impl* db;
 	unsigned int i;
+	char ers_name[50];
 
 #ifdef DB_ENABLE_STATS
 	DB_COUNTSTAT(db_alloc);
@@ -2517,7 +2533,7 @@ DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsi
 		case DB_UINT64: DB_COUNTSTAT(db_uint64_alloc); break;
 	}
 #endif /* DB_ENABLE_STATS */
-	CREATE(db, struct DBMap_impl, 1);
+	db = ers_alloc(db_alloc_ers, struct DBMap_impl);
 
 	options = db_fix_options(type, options);
 	/* Interface of the database */
@@ -2548,7 +2564,8 @@ DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsi
 	db->free_max = 0;
 	db->free_lock = 0;
 	/* Other */
-	db->nodes = ers_new(sizeof(struct dbn),"db.c::db_alloc",ERS_OPT_NONE);
+	snprintf(ers_name, 50, "db_alloc:nodes:%s:%s:%d",func,file,line);
+	db->nodes = ers_new(sizeof(struct dbn),ers_name,ERS_OPT_WAIT|ERS_OPT_FREE_NAME|ERS_OPT_CLEAN);
 	db->cmp = db_default_cmp(type);
 	db->hash = db_default_hash(type);
 	db->release = db_default_release(type, options);
@@ -2740,8 +2757,11 @@ void* db_data2ptr(DBData *data)
  * @public
  * @see #db_final(void)
  */
-void db_init(void)
-{
+void db_init(void) {
+	db_iterator_ers = ers_new(sizeof(struct DBIterator_impl),"db.c::db_iterator_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+	db_alloc_ers = ers_new(sizeof(struct DBMap_impl),"db.c::db_alloc_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
+	ers_chunk_size(db_alloc_ers, 50);
+	ers_chunk_size(db_iterator_ers, 10);
 	DB_COUNTSTAT(db_init);
 }
 
@@ -2844,6 +2864,8 @@ void db_final(void)
 			stats.db_data2ui,         stats.db_data2ptr,
 			stats.db_init,            stats.db_final);
 #endif /* DB_ENABLE_STATS */
+	ers_destroy(db_iterator_ers);
+	ers_destroy(db_alloc_ers);
 }
 
 // Link DB System - jAthena
@@ -2868,29 +2890,24 @@ void linkdb_insert( struct linkdb_node** head, void *key, void* data)
 	node->data = data;
 }
 
-int linkdb_vforeach( struct linkdb_node** head, LinkDBFunc func, va_list ap) {
+void linkdb_vforeach( struct linkdb_node** head, LinkDBFunc func, va_list ap) {
 	struct linkdb_node *node;
-	int retCount = 0;
-	if( head == NULL )
-		return 0;
+	if( head == NULL ) return;
 	node = *head;
 	while ( node ) {
 		va_list argscopy;
 		va_copy(argscopy, ap);
-		retCount += func(node->key, node->data, argscopy);
+		func(node->key, node->data, argscopy);
 		va_end(argscopy);
 		node = node->next;
 	}
-	return retCount;
 }
 
-int linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  ) {
+void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...) {
 	va_list ap;
-	int retCount = 0;
 	va_start(ap, func);
-	retCount = linkdb_vforeach(head, func, ap);
+	linkdb_vforeach(head, func, ap);
 	va_end(ap);
-	return retCount;
 }
 
 void* linkdb_search( struct linkdb_node** head, void *key)

+ 42 - 40
src/common/db.h

@@ -70,10 +70,10 @@
  * @see #db_custom_release(DBRelease)
  */
 typedef enum DBRelease {
-	DB_RELEASE_NOTHING = 0,
-	DB_RELEASE_KEY     = 1,
-	DB_RELEASE_DATA    = 2,
-	DB_RELEASE_BOTH    = 3
+	DB_RELEASE_NOTHING = 0x0,
+	DB_RELEASE_KEY     = 0x1,
+	DB_RELEASE_DATA    = 0x2,
+	DB_RELEASE_BOTH    = DB_RELEASE_KEY|DB_RELEASE_DATA,
 } DBRelease;
 
 /**
@@ -126,13 +126,13 @@ typedef enum DBType {
  * @see #db_alloc(const char *,int,DBType,DBOptions,unsigned short)
  */
 typedef enum DBOptions {
-	DB_OPT_BASE            = 0,
-	DB_OPT_DUP_KEY         = 1,
-	DB_OPT_RELEASE_KEY     = 2,
-	DB_OPT_RELEASE_DATA    = 4,
-	DB_OPT_RELEASE_BOTH    = 6,
-	DB_OPT_ALLOW_NULL_KEY  = 8,
-	DB_OPT_ALLOW_NULL_DATA = 16,
+	DB_OPT_BASE            = 0x00,
+	DB_OPT_DUP_KEY         = 0x01,
+	DB_OPT_RELEASE_KEY     = 0x02,
+	DB_OPT_RELEASE_DATA    = 0x04,
+	DB_OPT_RELEASE_BOTH    = DB_OPT_RELEASE_KEY|DB_OPT_RELEASE_DATA,
+	DB_OPT_ALLOW_NULL_KEY  = 0x08,
+	DB_OPT_ALLOW_NULL_DATA = 0x10,
 } DBOptions;
 
 /**
@@ -165,7 +165,7 @@ typedef union DBKey {
 typedef enum DBDataType {
 	DB_DATA_INT,
 	DB_DATA_UINT,
-	DB_DATA_PTR
+	DB_DATA_PTR,
 } DBDataType;
 
 /**
@@ -679,22 +679,22 @@ struct DBMap {
 #define ui64db_ensure(db,k,f) ( db_data2ptr((db)->ensure((db),db_ui642key(k),(f))) )
 
 // Database creation and destruction macros
-#define idb_alloc(opt)            db_alloc(__FILE__,__LINE__,DB_INT,(opt),sizeof(int))
-#define uidb_alloc(opt)           db_alloc(__FILE__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
-#define strdb_alloc(opt,maxlen)   db_alloc(__FILE__,__LINE__,DB_STRING,(opt),(maxlen))
-#define stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__LINE__,DB_ISTRING,(opt),(maxlen))
-#define i64db_alloc(opt)          db_alloc(__FILE__,__LINE__,DB_INT64,(opt),sizeof(int64))
-#define ui64db_alloc(opt)         db_alloc(__FILE__,__LINE__,DB_UINT64,(opt),sizeof(uint64))
+#define idb_alloc(opt)            db_alloc(__FILE__,__func__,__LINE__,DB_INT,(opt),sizeof(int))
+#define uidb_alloc(opt)           db_alloc(__FILE__,__func__,__LINE__,DB_UINT,(opt),sizeof(unsigned int))
+#define strdb_alloc(opt,maxlen)   db_alloc(__FILE__,__func__,__LINE__,DB_STRING,(opt),(maxlen))
+#define stridb_alloc(opt,maxlen)  db_alloc(__FILE__,__func__,__LINE__,DB_ISTRING,(opt),(maxlen))
+#define i64db_alloc(opt)          db_alloc(__FILE__,__func__,__LINE__,DB_INT64,(opt),sizeof(int64))
+#define ui64db_alloc(opt)         db_alloc(__FILE__,__func__,__LINE__,DB_UINT64,(opt),sizeof(uint64))
 #define db_destroy(db)            ( (db)->destroy((db),NULL) )
 // Other macros
-#define db_clear(db)        ( (db)->clear(db,NULL) )
+#define db_clear(db)        ( (db)->clear((db),NULL) )
 #define db_size(db)         ( (db)->size(db) )
 #define db_iterator(db)     ( (db)->iterator(db) )
-#define dbi_first(dbi)      ( db_data2ptr((dbi)->first(dbi,NULL)) )
-#define dbi_last(dbi)       ( db_data2ptr((dbi)->last(dbi,NULL)) )
-#define dbi_next(dbi)       ( db_data2ptr((dbi)->next(dbi,NULL)) )
-#define dbi_prev(dbi)       ( db_data2ptr((dbi)->prev(dbi,NULL)) )
-#define dbi_remove(dbi)     ( (dbi)->remove(dbi,NULL) )
+#define dbi_first(dbi)      ( db_data2ptr((dbi)->first((dbi),NULL)) )
+#define dbi_last(dbi)       ( db_data2ptr((dbi)->last((dbi),NULL)) )
+#define dbi_next(dbi)       ( db_data2ptr((dbi)->next((dbi),NULL)) )
+#define dbi_prev(dbi)       ( db_data2ptr((dbi)->prev((dbi),NULL)) )
+#define dbi_remove(dbi)     ( (dbi)->remove((dbi),NULL) )
 #define dbi_exists(dbi)     ( (dbi)->exists(dbi) )
 #define dbi_destroy(dbi)    ( (dbi)->destroy(dbi) )
 
@@ -786,7 +786,7 @@ DBReleaser db_custom_release(DBRelease which);
 
 /**
  * Allocate a new database of the specified type.
- * It uses the default comparator, hasher and releaser of the specified 
+ * It uses the default comparator, hasher and releaser of the specified
  * database type and fixed options.
  * NOTE: the options are fixed by {@link #db_fix_options(DBType,DBOptions)}
  * before creating the database.
@@ -794,7 +794,7 @@ DBReleaser db_custom_release(DBRelease which);
  * @param line Line of the file where the database is being allocated
  * @param type Type of database
  * @param options Options of the database
- * @param maxlen Maximum length of the string to be used as key in string 
+ * @param maxlen Maximum length of the string to be used as key in string
  *          databases. If 0, the maximum number of maxlen is used (64K).
  * @return The interface of the database
  * @public
@@ -805,7 +805,7 @@ DBReleaser db_custom_release(DBRelease which);
  * @see #db_default_release(DBType,DBOptions)
  * @see #db_fix_options(DBType,DBOptions)
  */
-DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsigned short maxlen);
+DBMap* db_alloc(const char *file, const char *func, int line, DBType type, DBOptions options, unsigned short maxlen);
 
 /**
  * Manual cast from 'int' to the union DBKey.
@@ -921,15 +921,15 @@ struct linkdb_node {
 	void               *data;
 };
 
-typedef int (*LinkDBFunc)(void* key, void* data, va_list args);
+typedef void (*LinkDBFunc)(void* key, void* data, va_list args);
 
-void  linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
-void  linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
-void* linkdb_search ( struct linkdb_node** head, void *key);
-void* linkdb_erase  ( struct linkdb_node** head, void *key);
-void  linkdb_final  ( struct linkdb_node** head );
-int   linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
-int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
+void  linkdb_insert  (struct linkdb_node** head, void *key, void* data); // Doesn't take into account duplicate keys
+void  linkdb_replace (struct linkdb_node** head, void *key, void* data); // Takes into account duplicate keys
+void* linkdb_search  (struct linkdb_node** head, void *key);
+void* linkdb_erase   (struct linkdb_node** head, void *key);
+void  linkdb_final   (struct linkdb_node** head);
+void  linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
+void  linkdb_foreach (struct linkdb_node** head, LinkDBFunc func, ...);
 
 
 
@@ -1132,8 +1132,8 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 	do{ \
 		if( (__n) > VECTOR_CAPACITY(__vec) ) \
 		{ /* increase size */ \
-			if( VECTOR_CAPACITY(__vec) == 0 ) SET_POINTER(VECTOR_DATA(__vec), aMalloc((__n)*sizeof(VECTOR_FIRST(__vec)))); /* allocate new */ \
-			else SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+			if( VECTOR_CAPACITY(__vec) == 0 ) VECTOR_DATA(__vec) = aMalloc((__n)*sizeof(VECTOR_FIRST(__vec))); /* allocate new */ \
+			else VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
 			memset(VECTOR_DATA(__vec)+VECTOR_LENGTH(__vec), 0, (VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec))*sizeof(VECTOR_FIRST(__vec))); /* clear new data */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 		} \
@@ -1145,7 +1145,7 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 		} \
 		else if( (__n) < VECTOR_CAPACITY(__vec) ) \
 		{ /* reduce size */ \
-			SET_POINTER(VECTOR_DATA(__vec), aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec)))); /* reallocate */ \
+			VECTOR_DATA(__vec) = aRealloc(VECTOR_DATA(__vec),(__n)*sizeof(VECTOR_FIRST(__vec))); /* reallocate */ \
 			VECTOR_CAPACITY(__vec) = (__n); /* update capacity */ \
 			if( VECTOR_LENGTH(__vec) > (__n) ) VECTOR_LENGTH(__vec) = (__n); /* update length */ \
 		} \
@@ -1162,8 +1162,10 @@ int   linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ...  );
 #define VECTOR_ENSURE(__vec,__n,__step) \
 	do{ \
 		size_t _empty_ = VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec); \
-		while( (__n) > _empty_ ) _empty_ += (__step); \
-		if( _empty_ != VECTOR_CAPACITY(__vec)-VECTOR_LENGTH(__vec) ) VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+		if( (__n) > _empty_ ) { \
+			while( (__n) > _empty_ ) _empty_ += (__step); \
+			VECTOR_RESIZE(__vec,_empty_+VECTOR_LENGTH(__vec)); \
+		} \
 	}while(0)
 
 

+ 144 - 64
src/common/ers.c

@@ -43,19 +43,24 @@
 
 #include "../common/cbasetypes.h"
 #include "../common/malloc.h" // CREATE, RECREATE, aMalloc, aFree
+#include "../common/nullpo.h"
 #include "../common/showmsg.h" // ShowMessage, ShowError, ShowFatalError, CL_BOLD, CL_NORMAL
 #include "ers.h"
 
+#include <stdlib.h>
+#include <string.h>
+
 #ifndef DISABLE_ERS
 
-#define ERS_ROOT_SIZE 256
-#define ERS_BLOCK_ENTRIES 4096
+#define ERS_BLOCK_ENTRIES 2048
 
 struct ers_list
 {
 	struct ers_list *Next;
 };
 
+struct ers_instance_t;
+
 typedef struct ers_cache
 {
 	// Allocated object size, including ers_list size
@@ -76,19 +81,27 @@ typedef struct ers_cache
 	// Free objects count
 	unsigned int Free;
 
-	// Used objects count
+	// Used blocks count
 	unsigned int Used;
 
+	// Objects in-use count
+	unsigned int UsedObjs;
+
+	// Default = ERS_BLOCK_ENTRIES, can be adjusted for performance for individual cache sizes.
+	unsigned int ChunkSize;
+
+	// Misc options, some options are shared from the instance
+	enum ERSOptions Options;
+
 	// Linked list
 	struct ers_cache *Next, *Prev;
 } ers_cache_t;
 
-typedef struct
-{
+struct ers_instance_t {
 	// Interface to ERS
 	struct eri VTable;
-	
-	// Name, used for debbuging purpouses
+
+	// Name, used for debugging purposes
 	char *Name;
 
 	// Misc options
@@ -99,18 +112,23 @@ typedef struct
 
 	// Count of objects in use, used for detecting memory leaks
 	unsigned int Count;
-} ers_instance_t;
+
+	struct ers_instance_t *Next, *Prev;
+};
 
 
 // Array containing a pointer for all ers_cache structures
-static ers_cache_t *CacheList;
+static ers_cache_t *CacheList = NULL;
+static struct ers_instance_t *InstanceList = NULL;
 
-static ers_cache_t *ers_find_cache(unsigned int size)
-{
+/**
+ * @param Options the options from the instance seeking a cache, we use it to give it a cache with matching configuration
+ **/
+static ers_cache_t *ers_find_cache(unsigned int size, enum ERSOptions Options) {
 	ers_cache_t *cache;
 
 	for (cache = CacheList; cache; cache = cache->Next)
-		if (cache->ObjectSize == size)
+		if ( cache->ObjectSize == size && cache->Options == ( Options & ERS_CACHE_OPTIONS ) )
 			return cache;
 
 	CREATE(cache, ers_cache_t, 1);
@@ -120,8 +138,11 @@ static ers_cache_t *ers_find_cache(unsigned int size)
 	cache->Blocks = NULL;
 	cache->Free = 0;
 	cache->Used = 0;
+	cache->UsedObjs = 0;
 	cache->Max = 0;
-	
+	cache->ChunkSize = ERS_BLOCK_ENTRIES;
+	cache->Options = (Options & ERS_CACHE_OPTIONS);
+
 	if (CacheList == NULL)
 	{
 		CacheList = cache;
@@ -156,87 +177,80 @@ static void ers_free_cache(ers_cache_t *cache, bool remove)
 	aFree(cache);
 }
 
-static void *ers_obj_alloc_entry(ERS self)
+static void *ers_obj_alloc_entry(ERS *self)
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 	void *ret;
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_alloc_entry: NULL object, aborting entry freeing.\n");
 		return NULL;
 	}
 
-	if (instance->Cache->ReuseList != NULL)
-	{
+	if (instance->Cache->ReuseList != NULL) {
 		ret = (void *)((unsigned char *)instance->Cache->ReuseList + sizeof(struct ers_list));
 		instance->Cache->ReuseList = instance->Cache->ReuseList->Next;
-	} 
-	else if (instance->Cache->Free > 0) 
-	{
+	} else if (instance->Cache->Free > 0) {
 		instance->Cache->Free--;
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
-	} 
-	else 
-	{
-		if (instance->Cache->Used == instance->Cache->Max) 
-		{
+	} else {
+		if (instance->Cache->Used == instance->Cache->Max) {
 			instance->Cache->Max = (instance->Cache->Max * 4) + 3;
 			RECREATE(instance->Cache->Blocks, unsigned char *, instance->Cache->Max);
 		}
 
-		CREATE(instance->Cache->Blocks[instance->Cache->Used], unsigned char, instance->Cache->ObjectSize * ERS_BLOCK_ENTRIES);
+		CREATE(instance->Cache->Blocks[instance->Cache->Used], unsigned char, instance->Cache->ObjectSize * instance->Cache->ChunkSize);
 		instance->Cache->Used++;
 
-		instance->Cache->Free = ERS_BLOCK_ENTRIES -1;
+		instance->Cache->Free = instance->Cache->ChunkSize -1;
 		ret = &instance->Cache->Blocks[instance->Cache->Used - 1][instance->Cache->Free * instance->Cache->ObjectSize + sizeof(struct ers_list)];
 	}
 
 	instance->Count++;
+	instance->Cache->UsedObjs++;
 
 	return ret;
 }
 
-static void ers_obj_free_entry(ERS self, void *entry)
+static void ers_obj_free_entry(ERS *self, void *entry)
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 	struct ers_list *reuse = (struct ers_list *)((unsigned char *)entry - sizeof(struct ers_list));
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_free_entry: NULL object, aborting entry freeing.\n");
 		return;
-	} 
-	else if (entry == NULL) 
-	{
+	} else if (entry == NULL) {
 		ShowError("ers_obj_free_entry: NULL entry, nothing to free.\n");
 		return;
 	}
 
+	if( instance->Cache->Options & ERS_OPT_CLEAN )
+		memset((unsigned char*)reuse + sizeof(struct ers_list), 0, instance->Cache->ObjectSize - sizeof(struct ers_list));
+
 	reuse->Next = instance->Cache->ReuseList;
 	instance->Cache->ReuseList = reuse;
 	instance->Count--;
+	instance->Cache->UsedObjs--;
 }
 
-static size_t ers_obj_entry_size(ERS self)
+static size_t ers_obj_entry_size(ERS *self)
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_entry_size: NULL object, aborting entry freeing.\n");
 		return 0;
-	} 
+	}
 
 	return instance->Cache->ObjectSize;
 }
 
-static void ers_obj_destroy(ERS self)
+static void ers_obj_destroy(ERS *self)
 {
-	ers_instance_t *instance = (ers_instance_t *)self;
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
 
-	if (instance == NULL) 
-	{
+	if (instance == NULL) {
 		ShowError("ers_obj_destroy: NULL object, aborting entry freeing.\n");
 		return;
 	}
@@ -248,54 +262,120 @@ static void ers_obj_destroy(ERS self)
 	if (--instance->Cache->ReferenceCount <= 0)
 		ers_free_cache(instance->Cache, true);
 
+	if (instance->Next)
+		instance->Next->Prev = instance->Prev;
+
+	if (instance->Prev)
+		instance->Prev->Next = instance->Next;
+	else
+		InstanceList = instance->Next;
+
+	if( instance->Options & ERS_OPT_FREE_NAME )
+		aFree(instance->Name);
+
 	aFree(instance);
 }
 
-ERS ers_new(uint32 size, char *name, enum ERSOptions options)
+void ers_cache_size(ERS *self, unsigned int new_size) {
+	struct ers_instance_t *instance = (struct ers_instance_t *)self;
+
+	nullpo_retv(instance);
+
+	if( !(instance->Cache->Options&ERS_OPT_FLEX_CHUNK) ) {
+		ShowWarning("ers_cache_size: '%s' has adjusted its chunk size to '%d', however ERS_OPT_FLEX_CHUNK is missing!\n",instance->Name,new_size);
+	}
+
+	instance->Cache->ChunkSize = new_size;
+}
+
+
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options)
 {
-	ers_instance_t *instance;
-	CREATE(instance, ers_instance_t, 1);
+	struct ers_instance_t *instance;
+	CREATE(instance,struct ers_instance_t, 1);
 
 	size += sizeof(struct ers_list);
+
+#if ERS_ALIGNED > 1 // If it's aligned to 1-byte boundaries, no need to bother.
 	if (size % ERS_ALIGNED)
 		size += ERS_ALIGNED - size % ERS_ALIGNED;
+#endif
 
 	instance->VTable.alloc = ers_obj_alloc_entry;
 	instance->VTable.free = ers_obj_free_entry;
 	instance->VTable.entry_size = ers_obj_entry_size;
 	instance->VTable.destroy = ers_obj_destroy;
+	instance->VTable.chunk_size = ers_cache_size;
 
-	instance->Name = name;
+	instance->Name = ( options & ERS_OPT_FREE_NAME ) ? aStrdup(name) : name;
 	instance->Options = options;
 
-	instance->Cache = ers_find_cache(size);
+	instance->Cache = ers_find_cache(size,instance->Options);
+
 	instance->Cache->ReferenceCount++;
 
+	if (InstanceList == NULL) {
+		InstanceList = instance;
+	} else {
+		instance->Next = InstanceList;
+		instance->Next->Prev = instance;
+		InstanceList = instance;
+		InstanceList->Prev = NULL;
+	}
+
 	instance->Count = 0;
 
 	return &instance->VTable;
 }
 
-void ers_report(void)
-{
+void ers_report(void) {
 	ers_cache_t *cache;
-	int i = 0;
+	unsigned int cache_c = 0, blocks_u = 0, blocks_a = 0, memory_b = 0, memory_t = 0;
+#ifdef DEBUG
+	struct ers_instance_t *instance;
+	unsigned int instance_c = 0, instance_c_d = 0;
+
+	for (instance = InstanceList; instance; instance = instance->Next) {
+		instance_c++;
+		if( (instance->Options & ERS_OPT_WAIT) && !instance->Count )
+			continue;
+		instance_c_d++;
+		ShowMessage(CL_BOLD"[ERS Instance "CL_NORMAL""CL_WHITE"%s"CL_NORMAL""CL_BOLD" report]\n"CL_NORMAL, instance->Name);
+		ShowMessage("\tblock size        : %u\n", instance->Cache->ObjectSize);
+		ShowMessage("\tblocks being used : %u\n", instance->Count);
+		ShowMessage("\tmemory in use     : %.2f MB\n", instance->Count == 0 ? 0. : (double)((instance->Count * instance->Cache->ObjectSize)/1024)/1024);
+	}
+#endif
+
 	for (cache = CacheList; cache; cache = cache->Next) {
-		ShowMessage(CL_BOLD"[Entry manager #%u report]\n"CL_NORMAL, ++i);
+		cache_c++;
+		ShowMessage(CL_BOLD"[ERS Cache of size '"CL_NORMAL""CL_WHITE"%u"CL_NORMAL""CL_BOLD"' report]\n"CL_NORMAL, cache->ObjectSize);
 		ShowMessage("\tinstances          : %u\n", cache->ReferenceCount);
-		ShowMessage("\tblock array size   : %u\n", cache->ObjectSize);
-		ShowMessage("\tallocated blocks   : %u\n", cache->Free+cache->Used);
-		ShowMessage("\tentries being used : %u\n", cache->Used);
-		ShowMessage("\tunused entries     : %u\n", cache->Free);
+		ShowMessage("\tblocks in use      : %u/%u\n", cache->UsedObjs, cache->UsedObjs+cache->Free);
+		ShowMessage("\tblocks unused      : %u\n", cache->Free);
+		ShowMessage("\tmemory in use      : %.2f MB\n", cache->UsedObjs == 0 ? 0. : (double)((cache->UsedObjs * cache->ObjectSize)/1024)/1024);
+		ShowMessage("\tmemory allocated   : %.2f MB\n", (cache->Free+cache->UsedObjs) == 0 ? 0. : (double)(((cache->UsedObjs+cache->Free) * cache->ObjectSize)/1024)/1024);
+		blocks_u += cache->UsedObjs;
+		blocks_a += cache->UsedObjs + cache->Free;
+		memory_b += cache->UsedObjs * cache->ObjectSize;
+		memory_t += (cache->UsedObjs+cache->Free) * cache->ObjectSize;
 	}
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' caches in use\n",cache_c);
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks in use, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"'\n",blocks_u,(double)((memory_b)/1024)/1024);
+	ShowInfo("ers_report: '"CL_WHITE"%u"CL_NORMAL"' blocks total, consuming '"CL_WHITE"%.2f MB"CL_NORMAL"' \n",blocks_a,(double)((memory_t)/1024)/1024);
 }
 
-void ers_force_destroy_all(void)
-{
-	ers_cache_t *cache;
-	
-	for (cache = CacheList; cache; cache = cache->Next)
-			ers_free_cache(cache, false);
+/**
+ * Call on shutdown to clear remaining entries
+ **/
+void ers_final(void) {
+	struct ers_instance_t *instance = InstanceList, *next;
+
+	while( instance ) {
+		next = instance->Next;
+		ers_obj_destroy((ERS*)instance);
+		instance = next;
+	}
 }
 
 #endif

+ 24 - 18
src/common/ers.h

@@ -49,7 +49,7 @@
  *  ERS                   - Entry manager.                                   *
  *  ers_new               - Allocate an instance of an entry manager.        *
  *  ers_report            - Print a report about the current state.          *
- *  ers_force_destroy_all - Force the destruction of all the managers.       *
+ *  ers_final             - Clears the remainder of the managers.           *
 \*****************************************************************************/
 
 /**
@@ -71,8 +71,15 @@
 #endif /* not ERS_ALIGN_ENTRY */
 
 enum ERSOptions {
-	ERS_OPT_NONE           = 0,
-	ERS_OPT_CLEAR          = 1,/* silently clears any entries left in the manager upon destruction */
+	ERS_OPT_NONE        = 0x00,
+	ERS_OPT_CLEAR       = 0x01,/* silently clears any entries left in the manager upon destruction */
+	ERS_OPT_WAIT        = 0x02,/* wait for entries to come in order to list! */
+	ERS_OPT_FREE_NAME   = 0x04,/* name is dynamic memory, and should be freed */
+	ERS_OPT_CLEAN       = 0x08,/* clears used memory upon ers_free so that its all new to be reused on the next alloc */
+	ERS_OPT_FLEX_CHUNK  = 0x10,/* signs that it should look for its own cache given it'll have a dynamic chunk size, so that it doesn't affect the other ERS it'd otherwise be sharing */
+
+	/* Compound, is used to determine whether it should be looking for a cache of matching options */
+	ERS_CACHE_OPTIONS   = ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK,
 };
 
 /**
@@ -117,7 +124,9 @@ typedef struct eri {
 	 */
 	void (*destroy)(struct eri *self);
 
-} *ERS;
+	/* */
+	void (*chunk_size) (struct eri *self, unsigned int new_size);
+} ERS;
 
 #ifdef DISABLE_ERS
 // Use memory manager to allocate/free and disable other interface functions
@@ -125,17 +134,19 @@ typedef struct eri {
 #	define ers_free(obj,entry) aFree(entry)
 #	define ers_entry_size(obj) (size_t)0
 #	define ers_destroy(obj)
+#	define ers_chunk_size(obj,size)
 // Disable the public functions
 #	define ers_new(size,name,options) NULL
 #	define ers_report()
-#	define ers_force_destroy_all()
+#	define ers_final()
 #else /* not DISABLE_ERS */
 // These defines should be used to allow the code to keep working whenever
 // the system is disabled
-#	define ers_alloc(obj,type) (type *)(obj)->alloc(obj)
-#	define ers_free(obj,entry) (obj)->free((obj),(entry))
-#	define ers_entry_size(obj) (obj)->entry_size(obj)
-#	define ers_destroy(obj)    (obj)->destroy(obj)
+#	define ers_alloc(obj,type) ((type *)(obj)->alloc(obj))
+#	define ers_free(obj,entry) ((obj)->free((obj),(entry)))
+#	define ers_entry_size(obj) ((obj)->entry_size(obj))
+#	define ers_destroy(obj)    ((obj)->destroy(obj))
+#	define ers_chunk_size(obj,size) ((obj)->chunk_size((obj),(size)))
 
 /**
  * Get a new instance of the manager that handles the specified entry size.
@@ -147,7 +158,7 @@ typedef struct eri {
  * @param The requested size of the entry in bytes
  * @return Interface of the object
  */
-ERS ers_new(uint32 size, char *name, enum ERSOptions options);
+ERS *ers_new(uint32 size, char *name, enum ERSOptions options);
 
 /**
  * Print a report about the current state of the Entry Reusage System.
@@ -159,14 +170,9 @@ ERS ers_new(uint32 size, char *name, enum ERSOptions options);
 void ers_report(void);
 
 /**
- * Forcibly destroy all the entry managers, checking for nothing.
- * The system is left as if no instances or entries had ever been allocated.
- * All previous entries and instances of the managers become invalid.
- * The use of this is NOT recommended.
- * It should only be used in extreme situations to make shure all the memory 
- * allocated by this system is released.
- */
-void ers_force_destroy_all(void);
+ * Clears the remainder of the managers
+ **/
+void ers_final(void);
 #endif /* DISABLE_ERS / not DISABLE_ERS */
 
 #endif /* _ERS_H_ */

+ 1 - 2
src/map/npc.c

@@ -2035,6 +2035,7 @@ int npc_unload(struct npc_data* nd, bool single) {
 			aFree(nd->u.scr.timer_event);
 		if (nd->src_id == 0) {
 			if(nd->u.scr.script) {
+				script_stop_instances(nd->bl.id);
 				script_free_code(nd->u.scr.script);
 				nd->u.scr.script = NULL;
 			}
@@ -2048,8 +2049,6 @@ int npc_unload(struct npc_data* nd, bool single) {
 			guild_flag_remove(nd);
 	}
 
-	script_stop_sleeptimers(nd->bl.id);
-
 	aFree(nd);
 
 	return 0;

+ 99 - 111
src/map/script.c

@@ -265,8 +265,6 @@ typedef struct script_function {
 
 extern script_function buildin_func[];
 
-static struct linkdb_node* sleep_db;// int oid -> struct script_state*
-
 #ifdef BETA_THREAD_TEST
 /**
  * MySQL Query Slave
@@ -3407,11 +3405,12 @@ void script_free_code(struct script_code* code)
 /// @param rid Who is running the script (attached player)
 /// @param oid Where the code is being run (npc 'object')
 /// @return Script state
-struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid)
+struct script_state* script_alloc_state(struct script_code* rootscript, int pos, int rid, int oid)
 {
 	struct script_state* st;
-	CREATE(st, struct script_state, 1);
-	st->stack = (struct script_stack*)aMalloc(sizeof(struct script_stack));
+
+	st = ers_alloc(st_ers, struct script_state);
+	st->stack = ers_alloc(stack_ers, struct script_stack);
 	st->stack->sp = 0;
 	st->stack->sp_max = 64;
 	CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max);
@@ -3419,8 +3418,7 @@ struct script_state* script_alloc_state(struct script_code* script, int pos, int
 	st->stack->scope.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
 	st->stack->scope.arrays = NULL;
 	st->state = RUN;
-	st->script = script;
-	//st->scriptroot = script;
+	st->script = rootscript;
 	st->pos = pos;
 	st->rid = rid;
 	st->oid = oid;
@@ -3430,6 +3428,11 @@ struct script_state* script_alloc_state(struct script_code* script, int pos, int
 	if (!st->script->local.vars)
 		st->script->local.vars = i64db_alloc(DB_OPT_RELEASE_DATA);
 
+	st->id = next_id++;
+	active_scripts++;
+
+	idb_put(st_db, st->id, st);
+
 	return st;
 }
 
@@ -3438,31 +3441,38 @@ struct script_state* script_alloc_state(struct script_code* script, int pos, int
 /// @param st Script state
 void script_free_state(struct script_state* st)
 {
-	if(st->bk_st)
-	{// backup was not restored
-		ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
-	}
-	if( st->sleep.timer != INVALID_TIMER )
-		delete_timer(st->sleep.timer, run_script_timer);
-	script_free_vars(st->stack->scope.vars);
-	if (st->stack->scope.arrays)
-		st->stack->scope.arrays->destroy(st->stack->scope.arrays, script_free_array_db);
-	pop_stack(st, 0, st->stack->sp);
-	aFree(st->stack->stack_data);
-	aFree(st->stack);
-	st->stack = NULL;
-	st->pos = -1;
-	if (st->script) {
-		if (st->script->local.vars && !db_size(st->script->local.vars)) {
-			script_free_vars(st->script->local.vars);
-			st->script->local.vars = NULL;
-		}
-		if (st->script->local.arrays && !db_size(st->script->local.arrays)) {
-			st->script->local.arrays->destroy(st->script->local.arrays, script_free_array_db);
-			st->script->local.arrays = NULL;
+	if (idb_exists(st_db, st->id)) {
+		if (st->bk_st) // backup was not restored
+			ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+
+		if (st->sleep.timer != INVALID_TIMER)
+			delete_timer(st->sleep.timer, run_script_timer);
+		if (st->stack) {
+			script_free_vars(st->stack->scope.vars);
+			if (st->stack->scope.arrays)
+				st->stack->scope.arrays->destroy(st->stack->scope.arrays, script_free_array_db);
+			pop_stack(st, 0, st->stack->sp);
+			aFree(st->stack->stack_data);
+			ers_free(stack_ers, st->stack);
+			st->stack = NULL;
+		}
+		if (st->script) {
+			if (st->script->local.vars && !db_size(st->script->local.vars)) {
+				script_free_vars(st->script->local.vars);
+				st->script->local.vars = NULL;
+			}
+			if (st->script->local.arrays && !db_size(st->script->local.arrays)) {
+				st->script->local.arrays->destroy(st->script->local.arrays, script_free_array_db);
+				st->script->local.arrays = NULL;
+			}
 		}
+		st->pos = -1;
+
+		idb_remove(st_db, st->id);
+		ers_free(st_ers, st);
+		if (--active_scripts == 0)
+			next_id = 0;
 	}
-	aFree(st);
 }
 
 //
@@ -3932,35 +3942,22 @@ void run_script(struct script_code *rootscript, int pos, int rid, int oid)
 	run_script_main(st);
 }
 
-void script_stop_sleeptimers(int id)
+void script_stop_instances(int id)
 {
-	for(;;)
-	{
-		struct script_state* st = (struct script_state*)linkdb_erase(&sleep_db,(void*)__64BPRTSIZE(id));
-		if( st == NULL )
-			break; // no more sleep timers
-		script_free_state(st);
-	}
-}
+	DBIterator *iter;
+	struct script_state* st;
 
-/*==========================================
- * Delete the specified node from sleep_db
- *------------------------------------------*/
-struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n)
-{
-	struct linkdb_node *retnode;
+	if (!active_scripts)
+		return; // Don't even bother.
 
-	if( n == NULL)
-		return NULL;
-	if( n->prev == NULL )
-		sleep_db = n->next;
-	else
-		n->prev->next = n->next;
-	if( n->next )
-		n->next->prev = n->prev;
-	retnode = n->next;
-	aFree( n );
-	return retnode;		// The following; return retnode
+	iter = db_iterator(st_db);
+
+	for(st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter)) {
+		if (st->oid == id)
+			script_free_state(st);
+	}
+
+	dbi_destroy(iter);
 }
 
 /*==========================================
@@ -3968,23 +3965,14 @@ struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n)
  *------------------------------------------*/
 int run_script_timer(int tid, unsigned int tick, int id, intptr_t data)
 {
-	struct script_state *st     = (struct script_state *)data;
-	struct linkdb_node *node    = (struct linkdb_node *)sleep_db;
+	struct script_state *st = (struct script_state *)data;
 	TBL_PC *sd = map_id2sd(st->rid);
 
-	if((sd && sd->status.char_id != id) || (st->rid && !sd))
-	{	//Character mismatch. Cancel execution.
+	if ((sd && sd->status.char_id != id) || (st->rid && !sd)) { // Character mismatch. Cancel execution.
 		st->rid = 0;
 		st->state = END;
 	}
-	while( node && st->sleep.timer != INVALID_TIMER ) {
-		if( (int)__64BPRTSIZE(node->key) == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) {
-			script_erase_sleepdb(node);
-			st->sleep.timer = INVALID_TIMER;
-			break;
-		}
-		node = node->next;
-	}
+	st->sleep.timer = INVALID_TIMER;
 	if(st->state != RERUNLINE)
 		st->sleep.tick = 0;
 	run_script_main(st);
@@ -4175,9 +4163,7 @@ void run_script_main(struct script_state *st)
 		st->sleep.charid = sd?sd->status.char_id:0;
 		st->sleep.timer  = add_timer(gettick()+st->sleep.tick,
 			run_script_timer, st->sleep.charid, (intptr_t)st);
-		linkdb_insert(&sleep_db, (void*)__64BPRTSIZE(st->oid), st);
-	}
-	else if(st->state != END && st->rid){
+	} else if(st->state != END && st->rid) {
 		//Resume later (st is already attached to player).
 		if(st->bk_st) {
 			ShowWarning("Unable to restore stack! Double continuation!\n");
@@ -4593,6 +4579,9 @@ static void *queryThread_main(void *x) {
  *------------------------------------------*/
 void do_final_script() {
 	int i;
+	DBIterator *iter;
+	struct script_state *st;
+
 #ifdef DEBUG_HASH
 	if (battle_config.etc_log)
 	{
@@ -4656,15 +4645,11 @@ void do_final_script() {
 		aFree(generic_ui_array);
 	userfunc_db->destroy(userfunc_db, db_script_free_code_sub);
 	autobonus_db->destroy(autobonus_db, db_script_free_code_sub);
-	if(sleep_db) {
-		struct linkdb_node *n = (struct linkdb_node *)sleep_db;
-		while(n) {
-			struct script_state *st = (struct script_state *)n->data;
-			script_free_state(st);
-			n = n->next;
-		}
-		linkdb_final(&sleep_db);
-	}
+
+	iter = db_iterator(st_db);
+	for(st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter))
+		script_free_state(st);
+	dbi_destroy(iter);
 
 	if (str_data)
 		aFree(str_data);
@@ -4677,6 +4662,11 @@ void do_final_script() {
 
 	if( atcmd_binding_count != 0 )
 		aFree(atcmd_binding);
+
+	ers_destroy(st_ers);
+	ers_destroy(stack_ers);
+	db_destroy(st_db);
+
 #ifdef BETA_THREAD_TEST
 	/* QueryThread */
 	InterlockedIncrement(&queryThreadTerminate);
@@ -4705,10 +4695,16 @@ void do_final_script() {
  * Initialization
  *------------------------------------------*/
 void do_init_script(void) {
-	userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0);
-	scriptlabel_db=strdb_alloc(DB_OPT_DUP_KEY,50);
+	st_db = idb_alloc(DB_OPT_BASE);
+	userfunc_db = strdb_alloc(DB_OPT_DUP_KEY,0);
+	scriptlabel_db = strdb_alloc(DB_OPT_DUP_KEY,50);
 	autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0);
 	array_ers = ers_new(sizeof(struct script_array), "script.c:array_ers", ERS_OPT_NONE);
+	st_ers = ers_new(sizeof(struct script_state), "script.c::st_ers", ERS_OPT_NONE);
+	stack_ers = ers_new(sizeof(struct script_stack), "script.c::script_stack", ERS_OPT_NONE);
+
+	ers_chunk_size(st_ers, 10);
+	ers_chunk_size(stack_ers, 10);
 
 	mapreg_init();
 #ifdef BETA_THREAD_TEST
@@ -4738,6 +4734,8 @@ void do_init_script(void) {
 
 void script_reload(void) {
 	int i;
+	DBIterator *iter;
+	struct script_state *st;
 
 #ifdef BETA_THREAD_TEST
 	/* we're reloading so any queries undergoing should be...exterminated. */
@@ -4756,7 +4754,6 @@ void script_reload(void) {
 	LeaveSpinLock(&queryThreadLock);
 #endif
 
-
 	userfunc_db->clear(userfunc_db, db_script_free_code_sub);
 	db_clear(scriptlabel_db);
 
@@ -4771,15 +4768,12 @@ void script_reload(void) {
 
 	atcmd_binding_count = 0;
 
-	if(sleep_db) {
-		struct linkdb_node *n = (struct linkdb_node *)sleep_db;
-		while(n) {
-			struct script_state *st = (struct script_state *)n->data;
-			script_free_state(st);
-			n = n->next;
-		}
-		linkdb_final(&sleep_db);
-	}
+	iter = db_iterator(st_db);
+	for(st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter))
+		script_free_state(st);
+	dbi_destroy(iter);
+	db_clear(st_db);
+
 	mapreg_reload();
 }
 
@@ -17620,45 +17614,39 @@ BUILDIN_FUNC(sleep2)
 /// awake "<npc name>";
 BUILDIN_FUNC(awake)
 {
+	DBIterator *iter;
+	struct script_state *tst;
 	struct npc_data* nd;
-	struct linkdb_node *node = (struct linkdb_node *)sleep_db;
 
-	nd = npc_name2id(script_getstr(st, 2));
-	if( nd == NULL ) {
+	if ((nd = npc_name2id(script_getstr(st, 2))) == NULL) {
 		ShowError("awake: NPC \"%s\" not found\n", script_getstr(st, 2));
 		return 1;
 	}
 
-	while( node )
-	{
-		if( (int)__64BPRTSIZE(node->key) == nd->bl.id )
-		{// sleep timer for the npc
-			struct script_state* tst = (struct script_state*)node->data;
+	iter = db_iterator(st_db);
+
+	for (tst = dbi_first(iter); dbi_exists(iter); tst = dbi_next(iter)) {
+		if (tst->oid == nd->bl.id) {
 			TBL_PC* sd = map_id2sd(tst->rid);
 
-			if( tst->sleep.timer == INVALID_TIMER )
-			{// already awake ???
-				node = node->next;
+			if (tst->sleep.timer == INVALID_TIMER) { // already awake ???
 				continue;
 			}
-			if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd))
-			{// char not online anymore / another char of the same account is online - Cancel execution
+			if ((sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd)) {
+				// char not online anymore / another char of the same account is online - Cancel execution
 				tst->state = END;
 				tst->rid = 0;
 			}
 
 			delete_timer(tst->sleep.timer, run_script_timer);
-			node = script_erase_sleepdb(node);
 			tst->sleep.timer = INVALID_TIMER;
-			if(tst->state != RERUNLINE)
+			if (tst->state != RERUNLINE)
 				tst->sleep.tick = 0;
 			run_script_main(tst);
 		}
-		else
-		{
-			node = node->next;
-		}
 	}
+	dbi_destroy(iter);
+
 	return SCRIPT_CMD_SUCCESS;
 }
 

+ 9 - 2
src/map/script.h

@@ -129,6 +129,7 @@
 enum { LABEL_NEXTLINE = 1, LABEL_START };
 
 struct map_session_data;
+struct eri;
 
 extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
 extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
@@ -267,6 +268,7 @@ struct script_state {
 	unsigned npc_item_flag : 1;
 	unsigned mes_active : 1;  // Store if invoking character has a NPC dialog box open.
 	char* funcname; // Stores the current running function name
+	unsigned int id;
 };
 
 struct script_reg {
@@ -295,6 +297,11 @@ enum script_parse_options {
  * used to generate quick script_array entries
  **/
 struct eri *array_ers;
+DBMap *st_db;
+unsigned int active_scripts;
+unsigned int next_id;
+struct eri *st_ers;
+struct eri *stack_ers;
 
 const char* skip_space(const char* p);
 void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos);
@@ -312,11 +319,11 @@ void pop_stack(struct script_state* st, int start, int end);
 int run_script_timer(int tid, unsigned int tick, int id, intptr_t data);
 void run_script_main(struct script_state *st);
 
-void script_stop_sleeptimers(int id);
+void script_stop_instances(int id);
 struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
 void script_free_code(struct script_code* code);
 void script_free_vars(struct DBMap *storage);
-struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid);
+struct script_state* script_alloc_state(struct script_code* rootscript, int pos, int rid, int oid);
 void script_free_state(struct script_state* st);
 
 struct DBMap* script_get_label_db(void);