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