queue.cpp

Go to the documentation of this file.
00001 /* $Id: queue.cpp 11691 2007-12-25 09:48:53Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "queue.h"
00008 #include "core/alloc_func.hpp"
00009 
00010 
00011 /*
00012  * Insertion Sorter
00013  */
00014 
00015 static void InsSort_Clear(Queue* q, bool free_values)
00016 {
00017   InsSortNode* node = q->data.inssort.first;
00018   InsSortNode* prev;
00019 
00020   while (node != NULL) {
00021     if (free_values) free(node->item);
00022     prev = node;
00023     node = node->next;
00024     free(prev);
00025   }
00026   q->data.inssort.first = NULL;
00027 }
00028 
00029 static void InsSort_Free(Queue* q, bool free_values)
00030 {
00031   q->clear(q, free_values);
00032 }
00033 
00034 static bool InsSort_Push(Queue* q, void* item, int priority)
00035 {
00036   InsSortNode* newnode = MallocT<InsSortNode>(1);
00037 
00038   if (newnode == NULL) return false;
00039   newnode->item = item;
00040   newnode->priority = priority;
00041   if (q->data.inssort.first == NULL ||
00042       q->data.inssort.first->priority >= priority) {
00043     newnode->next = q->data.inssort.first;
00044     q->data.inssort.first = newnode;
00045   } else {
00046     InsSortNode* node = q->data.inssort.first;
00047     while (node != NULL) {
00048       if (node->next == NULL || node->next->priority >= priority) {
00049         newnode->next = node->next;
00050         node->next = newnode;
00051         break;
00052       }
00053       node = node->next;
00054     }
00055   }
00056   return true;
00057 }
00058 
00059 static void* InsSort_Pop(Queue* q)
00060 {
00061   InsSortNode* node = q->data.inssort.first;
00062   void* result;
00063 
00064   if (node == NULL) return NULL;
00065   result = node->item;
00066   q->data.inssort.first = q->data.inssort.first->next;
00067   assert(q->data.inssort.first == NULL || q->data.inssort.first->priority >= node->priority);
00068   free(node);
00069   return result;
00070 }
00071 
00072 static bool InsSort_Delete(Queue* q, void* item, int priority)
00073 {
00074   return false;
00075 }
00076 
00077 void init_InsSort(Queue* q)
00078 {
00079   q->push = InsSort_Push;
00080   q->pop = InsSort_Pop;
00081   q->del = InsSort_Delete;
00082   q->clear = InsSort_Clear;
00083   q->free = InsSort_Free;
00084   q->data.inssort.first = NULL;
00085 }
00086 
00087 
00088 /*
00089  * Binary Heap
00090  * For information, see: http://www.policyalmanac.org/games/binaryHeaps.htm
00091  */
00092 
00093 #define BINARY_HEAP_BLOCKSIZE (1 << BINARY_HEAP_BLOCKSIZE_BITS)
00094 #define BINARY_HEAP_BLOCKSIZE_MASK (BINARY_HEAP_BLOCKSIZE - 1)
00095 
00096 /* To make our life easy, we make the next define
00097  *  Because Binary Heaps works with array from 1 to n,
00098  *  and C with array from 0 to n-1, and we don't like typing
00099  *  q->data.binaryheap.elements[i - 1] every time, we use this define. */
00100 #define BIN_HEAP_ARR(i) q->data.binaryheap.elements[((i) - 1) >> BINARY_HEAP_BLOCKSIZE_BITS][((i) - 1) & BINARY_HEAP_BLOCKSIZE_MASK]
00101 
00102 static void BinaryHeap_Clear(Queue* q, bool free_values)
00103 {
00104   /* Free all items if needed and free all but the first blocks of memory */
00105   uint i;
00106   uint j;
00107 
00108   for (i = 0; i < q->data.binaryheap.blocks; i++) {
00109     if (q->data.binaryheap.elements[i] == NULL) {
00110       /* No more allocated blocks */
00111       break;
00112     }
00113     /* For every allocated block */
00114     if (free_values) {
00115       for (j = 0; j < (1 << BINARY_HEAP_BLOCKSIZE_BITS); j++) {
00116         /* For every element in the block */
00117         if ((q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS) == i &&
00118             (q->data.binaryheap.size & BINARY_HEAP_BLOCKSIZE_MASK) == j) {
00119           break; // We're past the last element
00120         }
00121         free(q->data.binaryheap.elements[i][j].item);
00122       }
00123     }
00124     if (i != 0) {
00125       /* Leave the first block of memory alone */
00126       free(q->data.binaryheap.elements[i]);
00127       q->data.binaryheap.elements[i] = NULL;
00128     }
00129   }
00130   q->data.binaryheap.size = 0;
00131   q->data.binaryheap.blocks = 1;
00132 }
00133 
00134 static void BinaryHeap_Free(Queue* q, bool free_values)
00135 {
00136   uint i;
00137 
00138   q->clear(q, free_values);
00139   for (i = 0; i < q->data.binaryheap.blocks; i++) {
00140     if (q->data.binaryheap.elements[i] == NULL) break;
00141     free(q->data.binaryheap.elements[i]);
00142   }
00143   free(q->data.binaryheap.elements);
00144 }
00145 
00146 static bool BinaryHeap_Push(Queue* q, void* item, int priority)
00147 {
00148 #ifdef QUEUE_DEBUG
00149   printf("[BinaryHeap] Pushing an element. There are %d elements left\n", q->data.binaryheap.size);
00150 #endif
00151 
00152   if (q->data.binaryheap.size == q->data.binaryheap.max_size) return false;
00153   assert(q->data.binaryheap.size < q->data.binaryheap.max_size);
00154 
00155   if (q->data.binaryheap.elements[q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS] == NULL) {
00156     /* The currently allocated blocks are full, allocate a new one */
00157     assert((q->data.binaryheap.size & BINARY_HEAP_BLOCKSIZE_MASK) == 0);
00158     q->data.binaryheap.elements[q->data.binaryheap.size >> BINARY_HEAP_BLOCKSIZE_BITS] = MallocT<BinaryHeapNode>(BINARY_HEAP_BLOCKSIZE);
00159     q->data.binaryheap.blocks++;
00160 #ifdef QUEUE_DEBUG
00161     printf("[BinaryHeap] Increasing size of elements to %d nodes\n", q->data.binaryheap.blocks *  BINARY_HEAP_BLOCKSIZE);
00162 #endif
00163   }
00164 
00165   /* Add the item at the end of the array */
00166   BIN_HEAP_ARR(q->data.binaryheap.size + 1).priority = priority;
00167   BIN_HEAP_ARR(q->data.binaryheap.size + 1).item = item;
00168   q->data.binaryheap.size++;
00169 
00170   /* Now we are going to check where it belongs. As long as the parent is
00171    * bigger, we switch with the parent */
00172   {
00173     BinaryHeapNode temp;
00174     int i;
00175     int j;
00176 
00177     i = q->data.binaryheap.size;
00178     while (i > 1) {
00179       /* Get the parent of this object (divide by 2) */
00180       j = i / 2;
00181       /* Is the parent bigger then the current, switch them */
00182       if (BIN_HEAP_ARR(i).priority <= BIN_HEAP_ARR(j).priority) {
00183         temp = BIN_HEAP_ARR(j);
00184         BIN_HEAP_ARR(j) = BIN_HEAP_ARR(i);
00185         BIN_HEAP_ARR(i) = temp;
00186         i = j;
00187       } else {
00188         /* It is not, we're done! */
00189         break;
00190       }
00191     }
00192   }
00193 
00194   return true;
00195 }
00196 
00197 static bool BinaryHeap_Delete(Queue* q, void* item, int priority)
00198 {
00199   uint i = 0;
00200 
00201 #ifdef QUEUE_DEBUG
00202   printf("[BinaryHeap] Deleting an element. There are %d elements left\n", q->data.binaryheap.size);
00203 #endif
00204 
00205   /* First, we try to find the item.. */
00206   do {
00207     if (BIN_HEAP_ARR(i + 1).item == item) break;
00208     i++;
00209   } while (i < q->data.binaryheap.size);
00210   /* We did not find the item, so we return false */
00211   if (i == q->data.binaryheap.size) return false;
00212 
00213   /* Now we put the last item over the current item while decreasing the size of the elements */
00214   q->data.binaryheap.size--;
00215   BIN_HEAP_ARR(i + 1) = BIN_HEAP_ARR(q->data.binaryheap.size + 1);
00216 
00217   /* Now the only thing we have to do, is resort it..
00218    * On place i there is the item to be sorted.. let's start there */
00219   {
00220     uint j;
00221     BinaryHeapNode temp;
00222     /* Because of the fact that Binary Heap uses array from 1 to n, we need to
00223      * increase i by 1
00224      */
00225     i++;
00226 
00227     for (;;) {
00228       j = i;
00229       /* Check if we have 2 childs */
00230       if (2 * j + 1 <= q->data.binaryheap.size) {
00231         /* Is this child smaller than the parent? */
00232         if (BIN_HEAP_ARR(j).priority >= BIN_HEAP_ARR(2 * j).priority) i = 2 * j;
00233         /* Yes, we _need_ to use i here, not j, because we want to have the smallest child
00234          *  This way we get that straight away! */
00235         if (BIN_HEAP_ARR(i).priority >= BIN_HEAP_ARR(2 * j + 1).priority) i = 2 * j + 1;
00236       /* Do we have one child? */
00237       } else if (2 * j <= q->data.binaryheap.size) {
00238         if (BIN_HEAP_ARR(j).priority >= BIN_HEAP_ARR(2 * j).priority) i = 2 * j;
00239       }
00240 
00241       /* One of our childs is smaller than we are, switch */
00242       if (i != j) {
00243         temp = BIN_HEAP_ARR(j);
00244         BIN_HEAP_ARR(j) = BIN_HEAP_ARR(i);
00245         BIN_HEAP_ARR(i) = temp;
00246       } else {
00247         /* None of our childs is smaller, so we stay here.. stop :) */
00248         break;
00249       }
00250     }
00251   }
00252 
00253   return true;
00254 }
00255 
00256 static void* BinaryHeap_Pop(Queue* q)
00257 {
00258   void* result;
00259 
00260 #ifdef QUEUE_DEBUG
00261   printf("[BinaryHeap] Popping an element. There are %d elements left\n", q->data.binaryheap.size);
00262 #endif
00263 
00264   if (q->data.binaryheap.size == 0) return NULL;
00265 
00266   /* The best item is always on top, so give that as result */
00267   result = BIN_HEAP_ARR(1).item;
00268   /* And now we should get rid of this item... */
00269   BinaryHeap_Delete(q, BIN_HEAP_ARR(1).item, BIN_HEAP_ARR(1).priority);
00270 
00271   return result;
00272 }
00273 
00274 void init_BinaryHeap(Queue* q, uint max_size)
00275 {
00276   assert(q != NULL);
00277   q->push = BinaryHeap_Push;
00278   q->pop = BinaryHeap_Pop;
00279   q->del = BinaryHeap_Delete;
00280   q->clear = BinaryHeap_Clear;
00281   q->free = BinaryHeap_Free;
00282   q->data.binaryheap.max_size = max_size;
00283   q->data.binaryheap.size = 0;
00284   /* We malloc memory in block of BINARY_HEAP_BLOCKSIZE
00285    *   It autosizes when it runs out of memory */
00286   q->data.binaryheap.elements = CallocT<BinaryHeapNode*>((max_size - 1) / BINARY_HEAP_BLOCKSIZE + 1);
00287   q->data.binaryheap.elements[0] = MallocT<BinaryHeapNode>(BINARY_HEAP_BLOCKSIZE);
00288   q->data.binaryheap.blocks = 1;
00289 #ifdef QUEUE_DEBUG
00290   printf("[BinaryHeap] Initial size of elements is %d nodes\n", BINARY_HEAP_BLOCKSIZE);
00291 #endif
00292 }
00293 
00294 // Because we don't want anyone else to bother with our defines
00295 #undef BIN_HEAP_ARR
00296 
00297 /*
00298  * Hash
00299  */
00300 
00301 void init_Hash(Hash* h, Hash_HashProc* hash, uint num_buckets)
00302 {
00303   /* Allocate space for the Hash, the buckets and the bucket flags */
00304   uint i;
00305 
00306   assert(h != NULL);
00307 #ifdef HASH_DEBUG
00308   debug("Allocated hash: %p", h);
00309 #endif
00310   h->hash = hash;
00311   h->size = 0;
00312   h->num_buckets = num_buckets;
00313   h->buckets = (HashNode*)MallocT<byte>(num_buckets * (sizeof(*h->buckets) + sizeof(*h->buckets_in_use)));
00314 #ifdef HASH_DEBUG
00315   debug("Buckets = %p", h->buckets);
00316 #endif
00317   h->buckets_in_use = (bool*)(h->buckets + num_buckets);
00318   for (i = 0; i < num_buckets; i++) h->buckets_in_use[i] = false;
00319 }
00320 
00321 
00322 void delete_Hash(Hash* h, bool free_values)
00323 {
00324   uint i;
00325 
00326   /* Iterate all buckets */
00327   for (i = 0; i < h->num_buckets; i++) {
00328     if (h->buckets_in_use[i]) {
00329       HashNode* node;
00330 
00331       /* Free the first value */
00332       if (free_values) free(h->buckets[i].value);
00333       node = h->buckets[i].next;
00334       while (node != NULL) {
00335         HashNode* prev = node;
00336 
00337         node = node->next;
00338         /* Free the value */
00339         if (free_values) free(prev->value);
00340         /* Free the node */
00341         free(prev);
00342       }
00343     }
00344   }
00345   free(h->buckets);
00346   /* No need to free buckets_in_use, it is always allocated in one
00347    * malloc with buckets */
00348 #ifdef HASH_DEBUG
00349   debug("Freeing Hash: %p", h);
00350 #endif
00351 }
00352 
00353 #ifdef HASH_STATS
00354 static void stat_Hash(const Hash* h)
00355 {
00356   uint used_buckets = 0;
00357   uint max_collision = 0;
00358   uint max_usage = 0;
00359   uint usage[200];
00360   uint i;
00361 
00362   for (i = 0; i < lengthof(usage); i++) usage[i] = 0;
00363   for (i = 0; i < h->num_buckets; i++) {
00364     uint collision = 0;
00365     if (h->buckets_in_use[i]) {
00366       const HashNode* node;
00367 
00368       used_buckets++;
00369       for (node = &h->buckets[i]; node != NULL; node = node->next) collision++;
00370       if (collision > max_collision) max_collision = collision;
00371     }
00372     if (collision >= lengthof(usage)) collision = lengthof(usage) - 1;
00373     usage[collision]++;
00374     if (collision > 0 && usage[collision] >= max_usage) {
00375       max_usage = usage[collision];
00376     }
00377   }
00378   printf(
00379     "---\n"
00380     "Hash size: %d\n"
00381     "Nodes used: %d\n"
00382     "Non empty buckets: %d\n"
00383     "Max collision: %d\n",
00384     h->num_buckets, h->size, used_buckets, max_collision
00385   );
00386   printf("{ ");
00387   for (i = 0; i <= max_collision; i++) {
00388     if (usage[i] > 0) {
00389       printf("%d:%d ", i, usage[i]);
00390 #if 0
00391       if (i > 0) {
00392         uint j;
00393 
00394         for (j = 0; j < usage[i] * 160 / 800; j++) putchar('#');
00395       }
00396       printf("\n");
00397 #endif
00398     }
00399   }
00400   printf ("}\n");
00401 }
00402 #endif
00403 
00404 void clear_Hash(Hash* h, bool free_values)
00405 {
00406   uint i;
00407 
00408 #ifdef HASH_STATS
00409   if (h->size > 2000) stat_Hash(h);
00410 #endif
00411 
00412   /* Iterate all buckets */
00413   for (i = 0; i < h->num_buckets; i++) {
00414     if (h->buckets_in_use[i]) {
00415       HashNode* node;
00416 
00417       h->buckets_in_use[i] = false;
00418       /* Free the first value */
00419       if (free_values) free(h->buckets[i].value);
00420       node = h->buckets[i].next;
00421       while (node != NULL) {
00422         HashNode* prev = node;
00423 
00424         node = node->next;
00425         if (free_values) free(prev->value);
00426         free(prev);
00427       }
00428     }
00429   }
00430   h->size = 0;
00431 }
00432 
00440 static HashNode* Hash_FindNode(const Hash* h, uint key1, uint key2, HashNode** prev_out)
00441 {
00442   uint hash = h->hash(key1, key2);
00443   HashNode* result = NULL;
00444 
00445 #ifdef HASH_DEBUG
00446   debug("Looking for %u, %u", key1, key2);
00447 #endif
00448   /* Check if the bucket is empty */
00449   if (!h->buckets_in_use[hash]) {
00450     if (prev_out != NULL) *prev_out = NULL;
00451     result = NULL;
00452   /* Check the first node specially */
00453   } else if (h->buckets[hash].key1 == key1 && h->buckets[hash].key2 == key2) {
00454     /* Save the value */
00455     result = h->buckets + hash;
00456     if (prev_out != NULL) *prev_out = NULL;
00457 #ifdef HASH_DEBUG
00458     debug("Found in first node: %p", result);
00459 #endif
00460   /* Check all other nodes */
00461   } else {
00462     HashNode* prev = h->buckets + hash;
00463     HashNode* node;
00464 
00465     for (node = prev->next; node != NULL; node = node->next) {
00466       if (node->key1 == key1 && node->key2 == key2) {
00467         /* Found it */
00468         result = node;
00469 #ifdef HASH_DEBUG
00470         debug("Found in other node: %p", result);
00471 #endif
00472         break;
00473       }
00474       prev = node;
00475     }
00476     if (prev_out != NULL) *prev_out = prev;
00477   }
00478 #ifdef HASH_DEBUG
00479   if (result == NULL) debug("Not found");
00480 #endif
00481   return result;
00482 }
00483 
00484 void* Hash_Delete(Hash* h, uint key1, uint key2)
00485 {
00486   void* result;
00487   HashNode* prev; // Used as output var for below function call
00488   HashNode* node = Hash_FindNode(h, key1, key2, &prev);
00489 
00490   if (node == NULL) {
00491     /* not found */
00492     result = NULL;
00493   } else if (prev == NULL) {
00494     /* It is in the first node, we can't free that one, so we free
00495      * the next one instead (if there is any)*/
00496     /* Save the value */
00497     result = node->value;
00498     if (node->next != NULL) {
00499       HashNode* next = node->next;
00500       /* Copy the second to the first */
00501       *node = *next;
00502       /* Free the second */
00503 #ifndef NOFREE
00504       free(next);
00505 #endif
00506     } else {
00507       /* This was the last in this bucket */
00508       /* Mark it as empty */
00509       uint hash = h->hash(key1, key2);
00510       h->buckets_in_use[hash] = false;
00511     }
00512   } else {
00513     /* It is in another node */
00514     /* Save the value */
00515     result = node->value;
00516     /* Link previous and next nodes */
00517     prev->next = node->next;
00518     /* Free the node */
00519 #ifndef NOFREE
00520     free(node);
00521 #endif
00522   }
00523   if (result != NULL) h->size--;
00524   return result;
00525 }
00526 
00527 
00528 void* Hash_Set(Hash* h, uint key1, uint key2, void* value)
00529 {
00530   HashNode* prev;
00531   HashNode* node = Hash_FindNode(h, key1, key2, &prev);
00532 
00533   if (node != NULL) {
00534     /* Found it */
00535     void* result = node->value;
00536 
00537     node->value = value;
00538     return result;
00539   }
00540   /* It is not yet present, let's add it */
00541   if (prev == NULL) {
00542     /* The bucket is still empty */
00543     uint hash = h->hash(key1, key2);
00544     h->buckets_in_use[hash] = true;
00545     node = h->buckets + hash;
00546   } else {
00547     /* Add it after prev */
00548     node = MallocT<HashNode>(1);
00549     prev->next = node;
00550   }
00551   node->next = NULL;
00552   node->key1 = key1;
00553   node->key2 = key2;
00554   node->value = value;
00555   h->size++;
00556   return NULL;
00557 }
00558 
00559 void* Hash_Get(const Hash* h, uint key1, uint key2)
00560 {
00561   HashNode* node = Hash_FindNode(h, key1, key2, NULL);
00562 
00563 #ifdef HASH_DEBUG
00564   debug("Found node: %p", node);
00565 #endif
00566   return (node != NULL) ? node->value : NULL;
00567 }
00568 
00569 uint Hash_Size(const Hash* h)
00570 {
00571   return h->size;
00572 }

Generated on Mon Sep 22 20:34:17 2008 for openttd by  doxygen 1.5.6