# queue.cpp

Go to the documentation of this file.

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