queue.cpp

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

Generated on Mon Feb 16 23:12:09 2009 for openttd by  doxygen 1.5.6