knight/shared/KeHashTable.cpp
author Matt Woodrow <pred@alliedmods.net>
Sun Oct 19 16:18:22 2008 +1300 (2008-10-19)
changeset 2366 ef798e137fe7
parent 2226 d10b38524be3
child 2541 0352326a1f3f
permissions -rw-r--r--
Updated rockthevote to use new OnClientConnected forward (bug 3311)
     1 #include <KeHashTable.h>
     2 
     3 using namespace Knight;
     4 
     5 struct KeHashNode
     6 {
     7 	KeHashNode *next;
     8 	uint32_t key_hash;
     9 	const void *key;
    10 	void *value;
    11 };
    12 
    13 namespace Knight
    14 {
    15 	class KeHashTable
    16 	{
    17 	public:
    18 		KeHashNode **buckets;
    19 		uint32_t num_buckets;
    20 		uint32_t shift;
    21 		uint32_t num_entries;
    22 		KeHashGenerator key_gen;
    23 		KeHashMarshal key_m;
    24 		KeHashMarshal val_m;
    25 		ke_allocator_t *node_alloc;
    26 		size_t key_offs;
    27 		size_t val_offs;
    28 		size_t node_size;
    29 		uint32_t grow_limit;
    30 		KeHashNode *free_list;
    31 		bool keep_free_list;
    32 	};
    33 }
    34 
    35 void *ke_DefHashMalloc(ke_allocator_t *alloc, size_t amt)
    36 {
    37 	return malloc(amt);
    38 }
    39 
    40 void ke_DefHashFree(ke_allocator_t *alloc, void *addr)
    41 {
    42 	free(addr);
    43 }
    44 
    45 ke_allocator_t s_DefHashAllocator = 
    46 {
    47 	ke_DefHashMalloc,
    48 	ke_DefHashFree,
    49 	NULL
    50 };
    51 
    52 KeHashTable *Knight::KE_CreateHashTable(
    53 	uint32_t bits,
    54 	KeHashGenerator key_gen,
    55 	const KeHashMarshal *key_marshal,
    56 	const KeHashMarshal *val_marshal,
    57 	ke_allocator_t *nodeAlloc,
    58 	bool keep_free_list)
    59 {
    60 	KeHashTable *table;
    61 
    62 	if (bits >= 27)
    63 	{
    64 		bits = 26;
    65 	}
    66 	else if (bits < 4)
    67 	{
    68 		bits = 4;
    69 	}
    70 
    71 	/* Validate marshals. */
    72 	if ((key_marshal->bytes != 0
    73 		 && key_marshal->ctor == NULL)
    74 		|| (val_marshal->bytes != 0
    75 			&& val_marshal->ctor == NULL))
    76 	{
    77 		return NULL;
    78 	}
    79 
    80 	table = new KeHashTable;
    81 	table->key_gen = key_gen;
    82 	table->key_m = *key_marshal;
    83 	table->val_m = *val_marshal;
    84 	table->num_entries = 0;
    85 	table->shift = bits;
    86 	table->node_alloc = nodeAlloc == NULL ? &s_DefHashAllocator : nodeAlloc;
    87 	table->num_buckets = (1 << bits);
    88 	table->grow_limit = (uint32_t)(0.9f * table->num_buckets);
    89 	table->keep_free_list = keep_free_list;
    90 	table->free_list = NULL;
    91 	table->buckets = (KeHashNode **)malloc(sizeof(KeHashNode *) * table->num_buckets);
    92 	memset(table->buckets, 0, sizeof(KeHashNode *) * table->num_buckets);
    93 
    94 	table->key_offs = sizeof(KeHashNode);
    95 	if (table->key_m.bytes != 0 && table->key_m.bytes % 8 != 0)
    96 	{
    97 		table->key_m.bytes += 8;
    98 		table->key_m.bytes -= (table->key_m.bytes % 8);
    99 	}
   100 	
   101 	table->val_offs = table->key_offs + table->key_m.bytes;
   102 	table->node_size = table->val_offs + table->val_m.bytes;
   103 
   104 	return table;
   105 }
   106 
   107 #define KE_GET_BUCKET(tbl, hsh) (&(tbl)->buckets[((hsh) * 0x9E3779B9) >> (tbl)->shift])
   108 
   109 KeHashNode **ke_HashInternalFind(KeHashTable *table, uint32_t key_hash, const void *key)
   110 {
   111 	KeHashNode *node;
   112 	KeHashNode **bucket;
   113 
   114 	bucket = KE_GET_BUCKET(table, key_hash);
   115 
   116 	/* :TODO: move to the front once found? */
   117 
   118 	while ((node = *bucket) != NULL)
   119 	{
   120 		if (node->key_hash == key_hash
   121 			&& ((table->key_m.cmp != NULL && table->key_m.cmp(node->key, key))
   122 				 || node->key == key))
   123 		{
   124 			return bucket;
   125 		}
   126 		bucket = &node->next;
   127 	}
   128 
   129 	return bucket;
   130 }
   131 
   132 void ke_ResizeHashTable(KeHashTable *table, uint32_t new_shift)
   133 {
   134 	uint32_t entries;
   135 	KeHashNode *next;
   136 	KeHashNode *node;
   137 	KeHashNode **rbucket;
   138 	KeHashNode **old_buckets;
   139 	uint32_t old_num_buckets;
   140 
   141 	/* Save old data */
   142 	old_num_buckets = table->num_buckets;
   143 	old_buckets = table->buckets;
   144 	entries = table->num_entries;
   145 
   146 	/* Save new data */
   147 	table->num_buckets = (1 << new_shift);
   148 	table->shift = new_shift;
   149 	table->grow_limit = (uint32_t)(0.9f * table->num_buckets);
   150 
   151 	table->buckets = (KeHashNode **)malloc(sizeof(KeHashNode *) * table->num_buckets);
   152 	memset(table->buckets, 0, sizeof(KeHashNode *) * table->num_buckets);
   153 
   154 	/* For each old bucket... */
   155 	for (uint32_t i = 0;
   156 		 i < old_num_buckets && entries != 0;
   157 		 i++)
   158 	{
   159 		node = old_buckets[i];
   160 		/* Get each item in its list... */
   161 		while (node != NULL)
   162 		{
   163 			next = node->next;
   164 			
   165 			/* Find the new replacement bucket it needs to go in. */
   166 			rbucket = KE_GET_BUCKET(table, node->key_hash);
   167 
   168 			/* Link this node to the next node in the new bucket. */
   169 			if (*rbucket == NULL)
   170 			{
   171 				node->next = NULL;
   172 			}
   173 			else
   174 			{
   175 				node->next = *rbucket;
   176 			}
   177 
   178 			/* Add us to the front of that bucket's list. */
   179 			*rbucket = node;
   180 
   181 			node = next;
   182 		}
   183 	}
   184 
   185 	free(old_buckets);
   186 }
   187 
   188 void Knight::KE_AddToHashTable(KeHashTable *table, const void *key, void *val)
   189 {
   190 	KeHashNode *node;
   191 	uint32_t key_hash;
   192 	KeHashNode **bucket;
   193 
   194 	key_hash = table->key_gen(key);
   195 	bucket = ke_HashInternalFind(table, key_hash, key);
   196 
   197 	if ((node = *bucket) != NULL)
   198 	{
   199 		/* Already in the table */
   200 		if ((table->val_m.cmp != NULL && table->val_m.cmp(node->value, val))
   201 			 || node->value == val)
   202 		{
   203 			return;
   204 		}
   205 
   206 		/* Destroy old value if it's set. */
   207 		if (node->value != NULL && table->val_m.dtor != NULL)
   208 		{
   209 			table->val_m.dtor(node->value);
   210 		}
   211 		
   212 		/* Construct or set the new value. */
   213 		if (table->val_m.bytes != 0)
   214 		{
   215 			table->val_m.ctor(node->value, val);
   216 		}
   217 		else
   218 		{
   219 			node->value = val;
   220 		}
   221 
   222 		return;
   223 	}
   224 
   225 	/* If we're overloaded, we may need to resize. 
   226 	 * Right now, we do this if we hit a .9 entry:buckets ratio.
   227 	 */
   228 	if (table->num_entries >= table->grow_limit)
   229 	{
   230 		ke_ResizeHashTable(table, table->shift << 1);
   231 		bucket = ke_HashInternalFind(table, key_hash, key);
   232 	}
   233 
   234 	if (table->free_list != NULL)
   235 	{
   236 		node = table->free_list;
   237 		table->free_list = node->next;
   238 	}
   239 	else
   240 	{
   241 		node = (KeHashNode *)table->node_alloc->alloc(table->node_alloc, table->node_size);
   242 	}
   243 	
   244 	if (table->key_m.bytes == 0)
   245 	{
   246 		node->key = key;
   247 	}
   248 	else
   249 	{
   250 		node->key = (char *)node + table->key_offs;
   251 		table->key_m.ctor((void *)node->key, key);
   252 	}
   253 
   254 	if (table->val_m.bytes == 0)
   255 	{
   256 		node->value = val;
   257 	}
   258 	else
   259 	{
   260 		node->value = (char *)node + table->val_offs;
   261 		table->val_m.ctor(node->value, val);
   262 	}
   263 
   264 	node->next = *bucket;
   265 	node->key_hash = key_hash;
   266 	*bucket = node;
   267 }
   268 
   269 inline void ke_CleanUpHashNode(KeHashTable *table, KeHashNode *node)
   270 {
   271 	/* Destroy old value if it's set. */
   272 	if (node->value != NULL && table->val_m.dtor != NULL)
   273 	{
   274 		table->val_m.dtor(node->value);
   275 	}
   276 
   277 	/* Destroy the key. */
   278 	if (table->key_m.dtor != NULL)
   279 	{
   280 		table->key_m.dtor(node->key);
   281 	}
   282 
   283 	/* Deallocate us as appropriate. */
   284 	if (table->keep_free_list)
   285 	{
   286 		node->next = table->free_list;
   287 		table->free_list = node;
   288 	}
   289 	else
   290 	{
   291 		table->node_alloc->dealloc(table->node_alloc, node);
   292 	}
   293 }
   294 
   295 void Knight::KE_RemoveFromHashTable(KeHashTable *table, const void *key)
   296 {
   297 	KeHashNode *node;
   298 	uint32_t key_hash;
   299 	KeHashNode **bucket;
   300 
   301 	key_hash = table->key_gen(key);
   302 	bucket = ke_HashInternalFind(table, key_hash, key);
   303 
   304 	if ((node = *bucket) == NULL)
   305 	{
   306 		return;
   307 	}
   308 
   309 	/* Link the bucket to its next (removing us). */
   310 	*bucket = node->next;
   311 
   312 	ke_CleanUpHashNode(table, node);
   313 }
   314 
   315 bool Knight::KE_FindInHashTable(KeHashTable *table, const void *key, void **value)
   316 {
   317 	KeHashNode *node;
   318 	uint32_t key_hash;
   319 	KeHashNode **bucket;
   320 
   321 	key_hash = table->key_gen(key);
   322 	bucket = ke_HashInternalFind(table, key_hash, key);
   323 
   324 	if ((node = *bucket) == NULL)
   325 	{
   326 		return false;
   327 	}
   328 
   329 	if (value != NULL)
   330 	{
   331 		*value = node->value;
   332 	}
   333 
   334 	return true;
   335 }
   336 
   337 void Knight::KE_DestroyHashTable(KeHashTable *table)
   338 {
   339 	KeHashNode *node, *next;
   340 
   341 	/* Turn off this caching! */
   342 	table->keep_free_list = false;
   343 
   344 	/* Find entries in buckets that need to be freed. */
   345 	for (uint32_t i = 0; i < table->num_buckets; i++)
   346 	{
   347 		node = table->buckets[i];
   348 
   349 		while (node != NULL)
   350 		{
   351 			next = node->next;
   352 			ke_CleanUpHashNode(table, node);
   353 			node = next;
   354 		}
   355 	}
   356 
   357 	/* Free the free list */
   358 	while (table->free_list != NULL)
   359 	{
   360 		next = table->free_list->next;
   361 		ke_CleanUpHashNode(table, table->free_list);
   362 		table->free_list = next;
   363 	}
   364 
   365 	/* Destroy everything now. */
   366 	free(table->buckets);
   367 	delete table;
   368 }
   369 
   370 void Knight::KE_ClearHashTable(KeHashTable *table)
   371 {
   372 	KeHashNode *node, *next;
   373 
   374 	/* Free every entry in the table. */
   375 	for (uint32_t i = 0; i < table->num_buckets; i++)
   376 	{
   377 		node = table->buckets[i];
   378 
   379 		while (node != NULL)
   380 		{
   381 			next = node->next;
   382 			ke_CleanUpHashNode(table, node);
   383 			node = next;
   384 		}
   385 	}
   386 }
   387 
   388 #if defined _MSC_VER && (defined _M_IX86 || defined _M_AMD64 || defined _M_X64)
   389 #pragma intrinsic(_rotl)
   390 #endif
   391 
   392 uint32_t Knight::KE_HashString(const void *str)
   393 {
   394 	uint32_t h;
   395 	const unsigned char *us;
   396 
   397 	h = 0;
   398 
   399 	for (us = (const unsigned char *)str; *us != 0; us++)
   400 	{
   401 #if defined _MSC_VER && (defined _M_IX86 || defined _M_AMD64 || defined _M_X64)
   402 		h = _rotl(h, 4) ^ *us;
   403 #else
   404 		h = ((h << 4) | (h >> 28)) ^ *us;
   405 #endif
   406 	}
   407 
   408 	return h;
   409 }