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