1 /*  NAME:
2         E3HashTable.c
3 
4     DESCRIPTION:
5         Quesa hash table.
6 
7         Implements a simple hash table, where items within the table are
8         keyed using four character constants. Collisions are handled by
9         a resizeable array of unsorted items stored at each slot.
10 
11 		Used by the class tree to store the class tree nodes, and to cache
12 		the methods for each node.
13 
14     COPYRIGHT:
15         Copyright (c) 1999-2004, Quesa Developers. All rights reserved.
16 
17         For the current release of Quesa, please see:
18 
19             <http://www.quesa.org/>
20 
21         Redistribution and use in source and binary forms, with or without
22         modification, are permitted provided that the following conditions
23         are met:
24 
25             o Redistributions of source code must retain the above copyright
26               notice, this list of conditions and the following disclaimer.
27 
28             o Redistributions in binary form must reproduce the above
29               copyright notice, this list of conditions and the following
30               disclaimer in the documentation and/or other materials provided
31               with the distribution.
32 
33             o Neither the name of Quesa nor the names of its contributors
34               may be used to endorse or promote products derived from this
35               software without specific prior written permission.
36 
37         THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38         "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39         LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40         A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41         OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42         SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
43         TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44         PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
45         LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
46         NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47         SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48     ___________________________________________________________________________
49 */
50 //=============================================================================
51 //      Include files
52 //-----------------------------------------------------------------------------
53 #include "E3Prefix.h"
54 #include "E3HashTable.h"
55 
56 
57 
58 
59 
60 //=============================================================================
61 //      Internal types
62 //-----------------------------------------------------------------------------
63 // An item stored within each node
64 typedef struct E3HashTableItem {
65 	TQ3ObjectType		theKey;						// Key for item
66 	void				*theItem;					// Data for item
67 } E3HashTableItem, *E3HashTableItemPtr;
68 
69 
70 // A node within a hash table
71 typedef struct E3HashTableNode {
72 	TQ3Uns32			numItems;					// Number of items in node
73 	E3HashTableItemPtr	theItems;					// Array of items for node
74 } E3HashTableNode, *E3HashTableNodePtr;
75 
76 
77 // A hash table
78 typedef struct E3HashTable {
79 	TQ3Uns32			collisionMax;				// Worst case collision count
80 	float				collisionAverage;			// Average collision count
81 	TQ3Uns32			numItems;					// Number of items in table
82 	TQ3Uns32			tableSize;					// Number of slots in table
83 	E3HashTableNodePtr	*theTable;
84 } E3HashTable;
85 
86 
87 
88 
89 
90 //=============================================================================
91 //      Internal functions
92 //-----------------------------------------------------------------------------
93 //      e3hash_find_node : Find the node for a given key.
94 //-----------------------------------------------------------------------------
95 //		Note :	Hash index algorithm inspired by John Punin's hash table code
96 //				for the W3's libwww library.  However, libwww needs to hash
97 //				strings of unknown length, whereas here we hash just 4 bytes,
98 //				which allows a mathematical simplification of the hash function.
99 //
100 //				Returns the address of a node pointer, rather than the node
101 //				pointer itself, since the node pointer may be NULL if the node
102 //				hasn't been allocated yet.
103 //-----------------------------------------------------------------------------
104 static E3HashTableNodePtr *
e3hash_find_node(E3HashTablePtr theTable,TQ3ObjectType theKey)105 e3hash_find_node(E3HashTablePtr theTable, TQ3ObjectType theKey)
106 {	TQ3Uns32		theIndex;
107 	TQ3Uns8			*thePtr;
108 
109 
110 
111 	// Validate our parameters
112 	Q3_ASSERT_VALID_PTR(theTable);
113 	Q3_ASSERT(theKey != kQ3ObjectTypeInvalid);
114 
115 
116 
117 	// Calculate the index for the node
118 	thePtr   = (TQ3Uns8 *) &theKey;
119 
120 	theIndex = (27*thePtr[0] + 9*thePtr[1] + 3*thePtr[2] + thePtr[3]) & (theTable->tableSize - 1);
121 
122 	Q3_ASSERT(theIndex >= 0 && theIndex < theTable->tableSize);
123 
124 
125 	// Return the address of the appropriate node within the table
126 	return(&theTable->theTable[theIndex]);
127 }
128 
129 
130 
131 
132 
133 //=============================================================================
134 //      e3hash_add_key : Add a key and its item to a node.
135 //-----------------------------------------------------------------------------
136 static TQ3Status
e3hash_add_key(E3HashTableNodePtr theNode,TQ3ObjectType theKey,void * theItem)137 e3hash_add_key(E3HashTableNodePtr theNode, TQ3ObjectType theKey, void *theItem)
138 {	TQ3Status		qd3dStatus;
139 
140 
141 
142 	// Validate our parameters
143 	Q3_ASSERT_VALID_PTR(theNode);
144 
145 
146 
147 	// Grow the node item array
148 	qd3dStatus = Q3Memory_Reallocate(&theNode->theItems,
149 										sizeof(E3HashTableItem) * (theNode->numItems + 1));
150 	if (qd3dStatus != kQ3Success)
151 		return(qd3dStatus);
152 
153 
154 
155 	// Save the item and its key at the end of the node's list,
156 	// and update the count for the node
157 	theNode->theItems[theNode->numItems].theKey  = theKey;
158 	theNode->theItems[theNode->numItems].theItem = theItem;
159 	theNode->numItems++;
160 
161 	return(kQ3Success);
162 }
163 
164 
165 
166 
167 
168 //=============================================================================
169 //      e3hash_update_stats : Update the table stats.
170 //-----------------------------------------------------------------------------
171 static void
e3hash_update_stats(E3HashTablePtr theTable)172 e3hash_update_stats(E3HashTablePtr theTable)
173 {	TQ3Uns32				n, itemCount, slotCount;
174 	E3HashTableNodePtr		theNode;
175 
176 
177 
178 	// Validate our parameters
179 	Q3_ASSERT_VALID_PTR(theTable);
180 
181 
182 
183 	// Recalculate the table stats
184 	theTable->collisionMax     = 0;
185 	theTable->collisionAverage = 0.0f;
186 	itemCount                  = 0;
187 	slotCount                  = 0;
188 
189 	for (n = 0; n < theTable->tableSize; n++)
190 		{
191 		theNode = theTable->theTable[n];
192 		if (theNode != NULL)
193 			{
194 			if (theNode->numItems > theTable->collisionMax)
195 				theTable->collisionMax = theNode->numItems;
196 
197 			itemCount += theNode->numItems;
198 			slotCount++;
199 			}
200 		}
201 
202 	theTable->collisionAverage = (float) itemCount / (float) slotCount;
203 }
204 
205 
206 
207 
208 
209 //=============================================================================
210 //      Public functions
211 //-----------------------------------------------------------------------------
212 //      E3HashTable_Create : Create a hash table.
213 //-----------------------------------------------------------------------------
214 #pragma mark -
215 E3HashTablePtr
E3HashTable_Create(TQ3Uns32 tableSize)216 E3HashTable_Create(TQ3Uns32 tableSize)
217 {	E3HashTablePtr		theTable;
218 
219 
220 
221 	// Validate our parameters
222 	Q3_ASSERT(tableSize != 0 && tableSize < 10000);
223 	Q3_ASSERT( (tableSize & (tableSize - 1)) == 0 );	// power of 2
224 
225 
226 
227 	// Create the table
228 	theTable = (E3HashTablePtr) Q3Memory_Allocate(sizeof(E3HashTable));
229 	if (theTable != NULL)
230 		{
231 		// Initialise the table
232 		theTable->collisionMax     = 0;
233 		theTable->collisionAverage = 0.0f;
234 		theTable->tableSize        = tableSize;
235 		theTable->numItems         = 0;
236 		theTable->theTable = (E3HashTableNodePtr *) Q3Memory_AllocateClear(sizeof(E3HashTableNodePtr)
237 																			* theTable->tableSize);
238 
239 
240 
241 		// Handle failure
242 		if (theTable->theTable == NULL)
243 			{
244 			Q3Memory_Free(&theTable);
245 			theTable = NULL;
246 			}
247 		}
248 
249 	return(theTable);
250 }
251 
252 
253 
254 
255 
256 //=============================================================================
257 //      E3HashTable_Destroy : Destroy a hash table.
258 //-----------------------------------------------------------------------------
259 void
E3HashTable_Destroy(E3HashTablePtr * theTable)260 E3HashTable_Destroy(E3HashTablePtr *theTable)
261 {	E3HashTablePtr			tablePtr;
262 	E3HashTableNodePtr		theNode;
263 	TQ3Uns32				n;
264 
265 
266 
267 	// Validate our parameters
268 	Q3_ASSERT_VALID_PTR(theTable);
269 	Q3_ASSERT_VALID_PTR(*theTable);
270 
271 
272 
273 	// Dispose of the table items
274 	tablePtr = *theTable;
275 	for (n = 0; n < tablePtr->tableSize; n++)
276 		{
277 		theNode = tablePtr->theTable[n];
278 		if (theNode != NULL)
279 			{
280 			Q3Memory_Free(&theNode->theItems);
281 			Q3Memory_Free(&tablePtr->theTable[n]);
282 			}
283 		}
284 
285 
286 
287 	// Dispose of the table itself
288 	Q3Memory_Free(&tablePtr->theTable);
289 	Q3Memory_Free(theTable);
290 }
291 
292 
293 
294 
295 
296 //=============================================================================
297 //      E3HashTable_Add : Add an item to a hash table.
298 //-----------------------------------------------------------------------------
299 //		Note :	The item indicated by theKey must not be present in the table,
300 //				and theItem must not be NULL.
301 //-----------------------------------------------------------------------------
302 TQ3Status
E3HashTable_Add(E3HashTablePtr theTable,TQ3ObjectType theKey,void * theItem)303 E3HashTable_Add(E3HashTablePtr theTable, TQ3ObjectType theKey, void *theItem)
304 {	TQ3Status				qd3dStatus;
305 	E3HashTableNodePtr		*theNode;
306 
307 
308 
309 	// Validate our parameters
310 	Q3_ASSERT_VALID_PTR(theTable);
311 	Q3_ASSERT(theItem != NULL);
312 
313 
314 
315 	// Make sure the item isn't already present
316 	Q3_ASSERT(E3HashTable_Find(theTable, theKey) == NULL);
317 
318 
319 
320 	// Find the node which should contain the item
321 	theNode = e3hash_find_node(theTable, theKey);
322 	Q3_ASSERT_VALID_PTR(theNode);
323 
324 
325 
326 	// If the node doesn't exist, create it
327 	if (*theNode == NULL)
328 		{
329 		*theNode = (E3HashTableNodePtr) Q3Memory_AllocateClear(sizeof(E3HashTableNode));
330 		if (*theNode == NULL)
331 			return(kQ3Failure);
332 		}
333 
334 
335 
336 	// Add the item to the node
337 	qd3dStatus = e3hash_add_key(*theNode, theKey, theItem);
338 	if (qd3dStatus != kQ3Success)
339 		return(qd3dStatus);
340 
341 
342 
343 	// Update the table
344 	theTable->numItems++;
345 	e3hash_update_stats(theTable);
346 
347 	return(kQ3Success);
348 }
349 
350 
351 
352 
353 
354 //=============================================================================
355 //      E3HashTable_Remove : Remove an item from a hash table.
356 //-----------------------------------------------------------------------------
357 //		Note : The item must be present in the hash table.
358 //-----------------------------------------------------------------------------
E3HashTable_Remove(E3HashTablePtr theTable,TQ3ObjectType theKey)359 void E3HashTable_Remove(E3HashTablePtr theTable, TQ3ObjectType theKey)
360 {	E3HashTableNodePtr		theNode, *nodePtr;
361 	TQ3Boolean				foundItem;
362 	TQ3Uns32				n;
363 
364 
365 
366 	// Validate our parameters
367 	Q3_ASSERT_VALID_PTR(theTable);
368 
369 
370 
371 	// Find the node which should contain the item
372 	nodePtr = e3hash_find_node(theTable, theKey);
373 	Q3_ASSERT_VALID_PTR(nodePtr);
374 
375 	theNode = *nodePtr;
376 	Q3_ASSERT_VALID_PTR(nodePtr);
377 
378 
379 
380 	// Remove the item from the node
381 	foundItem = kQ3False;
382 	for (n = 0; n < theNode->numItems && !foundItem; n++)
383 		{
384 		if (theKey == theNode->theItems[n].theKey)
385 			{
386 			// Shift the items above this item, if any, done by 1
387 			if (n != (theNode->numItems - 1))
388 				memmove(&theNode->theItems[n],
389 						&theNode->theItems[n+1],
390 						(theNode->numItems - n - 1) * sizeof(E3HashTableItem));
391 
392 
393 			// Decrement the node count and break
394 			Q3_ASSERT(theNode->numItems >= 1);
395 			theNode->numItems--;
396 			foundItem = kQ3True;
397 			}
398 		}
399 
400 
401 
402 	// Update the table
403 	Q3_ASSERT(foundItem);
404 	Q3_ASSERT(theTable->numItems >= 1);
405 
406 	if (foundItem)
407 		{
408 		theTable->numItems--;
409 		e3hash_update_stats(theTable);
410 		}
411 }
412 
413 
414 
415 
416 
417 //=============================================================================
418 //      E3HashTable_Find : Find an item within a hash table.
419 //-----------------------------------------------------------------------------
420 //		Note :	If the item can not be found, we will return NULL.
421 //-----------------------------------------------------------------------------
422 void *
E3HashTable_Find(E3HashTablePtr theTable,TQ3ObjectType theKey)423 E3HashTable_Find(E3HashTablePtr theTable, TQ3ObjectType theKey)
424 {	E3HashTableNodePtr		theNode, *nodePtr;
425 	E3HashTableItemPtr		theItem;
426 	TQ3Uns32				n, numItems;
427 
428 
429 
430 	// Validate our parameters
431 	Q3_ASSERT_VALID_PTR(theTable);
432 
433 
434 
435 	// Find the node which should contain the item
436 	nodePtr = e3hash_find_node(theTable, theKey);
437 
438 
439 
440 	// If the node is empty, we're done
441 	if (*nodePtr == NULL)
442 		return(NULL);
443 
444 	theNode = *nodePtr;
445 	Q3_ASSERT_VALID_PTR(theNode);
446 
447 
448 
449 	// Otherwise, look for the item
450 	theItem = theNode->theItems;
451 	numItems = theNode->numItems;
452 	for (n = 0; n < numItems; ++n)
453 		{
454 		if (theKey == theItem->theKey)
455 			return(theItem->theItem);
456 
457 		++theItem;
458 		}
459 
460 	return(NULL);
461 }
462 
463 
464 
465 
466 
467 //=============================================================================
468 //      E3HashTable_Iterate : Iterate over the items in a hash table.
469 //-----------------------------------------------------------------------------
470 TQ3Status
E3HashTable_Iterate(E3HashTablePtr theTable,TQ3HashTableIterator theIterator,void * userData)471 E3HashTable_Iterate(E3HashTablePtr theTable, TQ3HashTableIterator theIterator, void *userData)
472 {	TQ3Status				qd3dStatus = kQ3Success;
473 	E3HashTableItemPtr		theItem;
474 	E3HashTableNodePtr		theNode;
475 	TQ3Uns32				n, m;
476 
477 
478 
479 	// Validate our parameters
480 	Q3_ASSERT_VALID_PTR(theTable);
481 	Q3_ASSERT_VALID_PTR(theIterator);
482 
483 
484 
485 	// Iterate over the table
486 	for (n = 0; n < theTable->tableSize; n++)
487 		{
488 		theNode = theTable->theTable[n];
489 		if (theNode != NULL)
490 			{
491 			theItem = theNode->theItems;
492 			for (m = 0; m < theNode->numItems; m++)
493 				{
494 				qd3dStatus = theIterator(theTable, theItem->theKey, theItem->theItem, userData);
495 				if (qd3dStatus != kQ3Success)
496 					break;
497 
498 				theItem++;
499 				}
500 			}
501 		}
502 
503 	return(qd3dStatus);
504 }
505 
506 
507 
508 
509 
510 //=============================================================================
511 //      E3HashTable_GetCollisionMax : Get the max collision count for a table.
512 //-----------------------------------------------------------------------------
513 TQ3Uns32
E3HashTable_GetCollisionMax(E3HashTablePtr theTable)514 E3HashTable_GetCollisionMax(E3HashTablePtr theTable)
515 {
516 
517 
518 	// Validate our parameters
519 	Q3_ASSERT_VALID_PTR(theTable);
520 
521 
522 
523 	// Get the value
524 	return(theTable->collisionMax);
525 }
526 
527 
528 
529 
530 
531 //=============================================================================
532 //      E3HashTable_GetCollisionAverage : Get the average collision count.
533 //-----------------------------------------------------------------------------
534 float
E3HashTable_GetCollisionAverage(E3HashTablePtr theTable)535 E3HashTable_GetCollisionAverage(E3HashTablePtr theTable)
536 {
537 
538 
539 	// Validate our parameters
540 	Q3_ASSERT_VALID_PTR(theTable);
541 
542 
543 
544 	// Get the value
545 	return(theTable->collisionAverage);
546 }
547 
548 
549 
550 
551 
552 //=============================================================================
553 //      E3HashTable_GetNumItems : Get the number of items in a table.
554 //-----------------------------------------------------------------------------
555 TQ3Uns32
E3HashTable_GetNumItems(E3HashTablePtr theTable)556 E3HashTable_GetNumItems(E3HashTablePtr theTable)
557 {
558 
559 
560 	// Validate our parameters
561 	Q3_ASSERT_VALID_PTR(theTable);
562 
563 
564 
565 	// Get the value
566 	return(theTable->numItems);
567 }
568 
569 
570 
571 
572 
573 //=============================================================================
574 //      E3HashTable_GetTableSize : Get the table size for a table.
575 //-----------------------------------------------------------------------------
576 TQ3Uns32
E3HashTable_GetTableSize(E3HashTablePtr theTable)577 E3HashTable_GetTableSize(E3HashTablePtr theTable)
578 {
579 
580 
581 	// Validate our parameters
582 	Q3_ASSERT_VALID_PTR(theTable);
583 
584 
585 
586 	// Get the value
587 	return(theTable->tableSize);
588 }
589