queue.cpp

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

Generated on Mon Aug 30 19:36:58 2010 for OpenTTD by  doxygen 1.6.1