# 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 }