LCOV - code coverage report
Current view: top level - basic - prioq.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 137 147 93.2 %
Date: 2019-08-22 15:41:25 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : /*
       4             :  * Priority Queue
       5             :  * The prioq object implements a priority queue. That is, it orders objects by
       6             :  * their priority and allows O(1) access to the object with the highest
       7             :  * priority. Insertion and removal are Θ(log n). Optionally, the caller can
       8             :  * provide a pointer to an index which will be kept up-to-date by the prioq.
       9             :  *
      10             :  * The underlying algorithm used in this implementation is a Heap.
      11             :  */
      12             : 
      13             : #include <errno.h>
      14             : #include <stdlib.h>
      15             : 
      16             : #include "alloc-util.h"
      17             : #include "hashmap.h"
      18             : #include "prioq.h"
      19             : 
      20             : struct prioq_item {
      21             :         void *data;
      22             :         unsigned *idx;
      23             : };
      24             : 
      25             : struct Prioq {
      26             :         compare_func_t compare_func;
      27             :         unsigned n_items, n_allocated;
      28             : 
      29             :         struct prioq_item *items;
      30             : };
      31             : 
      32         197 : Prioq *prioq_new(compare_func_t compare_func) {
      33             :         Prioq *q;
      34             : 
      35         197 :         q = new(Prioq, 1);
      36         197 :         if (!q)
      37           0 :                 return q;
      38             : 
      39         197 :         *q = (Prioq) {
      40             :                 .compare_func = compare_func,
      41             :         };
      42             : 
      43         197 :         return q;
      44             : }
      45             : 
      46         569 : Prioq* prioq_free(Prioq *q) {
      47         569 :         if (!q)
      48         375 :                 return NULL;
      49             : 
      50         194 :         free(q->items);
      51         194 :         return mfree(q);
      52             : }
      53             : 
      54        2121 : int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
      55        2121 :         assert(q);
      56             : 
      57        2121 :         if (*q)
      58        1926 :                 return 0;
      59             : 
      60         195 :         *q = prioq_new(compare_func);
      61         195 :         if (!*q)
      62           0 :                 return -ENOMEM;
      63             : 
      64         195 :         return 0;
      65             : }
      66             : 
      67     2543115 : static void swap(Prioq *q, unsigned j, unsigned k) {
      68     2543115 :         assert(q);
      69     2543115 :         assert(j < q->n_items);
      70     2543115 :         assert(k < q->n_items);
      71             : 
      72     2543115 :         assert(!q->items[j].idx || *(q->items[j].idx) == j);
      73     2543115 :         assert(!q->items[k].idx || *(q->items[k].idx) == k);
      74             : 
      75     2543115 :         SWAP_TWO(q->items[j].data, q->items[k].data);
      76     2543115 :         SWAP_TWO(q->items[j].idx, q->items[k].idx);
      77             : 
      78     2543115 :         if (q->items[j].idx)
      79     2499397 :                 *q->items[j].idx = j;
      80             : 
      81     2543115 :         if (q->items[k].idx)
      82     2499397 :                 *q->items[k].idx = k;
      83     2543115 : }
      84             : 
      85      481583 : static unsigned shuffle_up(Prioq *q, unsigned idx) {
      86      481583 :         assert(q);
      87      481583 :         assert(idx < q->n_items);
      88             : 
      89      524268 :         while (idx > 0) {
      90             :                 unsigned k;
      91             : 
      92      454795 :                 k = (idx-1)/2;
      93             : 
      94      454795 :                 if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0)
      95      412110 :                         break;
      96             : 
      97       42685 :                 swap(q, idx, k);
      98       42685 :                 idx = k;
      99             :         }
     100             : 
     101      481583 :         return idx;
     102             : }
     103             : 
     104      420093 : static unsigned shuffle_down(Prioq *q, unsigned idx) {
     105      420093 :         assert(q);
     106             : 
     107     2500430 :         for (;;) {
     108             :                 unsigned j, k, s;
     109             : 
     110     2920523 :                 k = (idx+1)*2; /* right child */
     111     2920523 :                 j = k-1;       /* left child */
     112             : 
     113     2920523 :                 if (j >= q->n_items)
     114      355026 :                         break;
     115             : 
     116     2565497 :                 if (q->compare_func(q->items[j].data, q->items[idx].data) < 0)
     117             : 
     118             :                         /* So our left child is smaller than we are, let's
     119             :                          * remember this fact */
     120     2289395 :                         s = j;
     121             :                 else
     122      276102 :                         s = idx;
     123             : 
     124     5110072 :                 if (k < q->n_items &&
     125     2544575 :                     q->compare_func(q->items[k].data, q->items[s].data) < 0)
     126             : 
     127             :                         /* So our right child is smaller than we are, let's
     128             :                          * remember this fact */
     129     1181095 :                         s = k;
     130             : 
     131             :                 /* s now points to the smallest of the three items */
     132             : 
     133     2565497 :                 if (s == idx)
     134             :                         /* No swap necessary, we're done */
     135       65067 :                         break;
     136             : 
     137     2500430 :                 swap(q, idx, s);
     138     2500430 :                 idx = s;
     139             :         }
     140             : 
     141      420093 :         return idx;
     142             : }
     143             : 
     144       61490 : int prioq_put(Prioq *q, void *data, unsigned *idx) {
     145             :         struct prioq_item *i;
     146             :         unsigned k;
     147             : 
     148       61490 :         assert(q);
     149             : 
     150       61490 :         if (q->n_items >= q->n_allocated) {
     151             :                 unsigned n;
     152             :                 struct prioq_item *j;
     153             : 
     154         242 :                 n = MAX((q->n_items+1) * 2, 16u);
     155         242 :                 j = reallocarray(q->items, n, sizeof(struct prioq_item));
     156         242 :                 if (!j)
     157           0 :                         return -ENOMEM;
     158             : 
     159         242 :                 q->items = j;
     160         242 :                 q->n_allocated = n;
     161             :         }
     162             : 
     163       61490 :         k = q->n_items++;
     164       61490 :         i = q->items + k;
     165       61490 :         i->data = data;
     166       61490 :         i->idx = idx;
     167             : 
     168       61490 :         if (idx)
     169       57394 :                 *idx = k;
     170             : 
     171       61490 :         shuffle_up(q, k);
     172             : 
     173       61490 :         return 0;
     174             : }
     175             : 
     176       61490 : static void remove_item(Prioq *q, struct prioq_item *i) {
     177             :         struct prioq_item *l;
     178             : 
     179       61490 :         assert(q);
     180       61490 :         assert(i);
     181             : 
     182       61490 :         l = q->items + q->n_items - 1;
     183             : 
     184       61490 :         if (i == l)
     185             :                 /* Last entry, let's just remove it */
     186       17026 :                 q->n_items--;
     187             :         else {
     188             :                 unsigned k;
     189             : 
     190             :                 /* Not last entry, let's replace the last entry with
     191             :                  * this one, and reshuffle */
     192             : 
     193       44464 :                 k = i - q->items;
     194             : 
     195       44464 :                 i->data = l->data;
     196       44464 :                 i->idx = l->idx;
     197       44464 :                 if (i->idx)
     198       40369 :                         *i->idx = k;
     199       44464 :                 q->n_items--;
     200             : 
     201       44464 :                 k = shuffle_down(q, k);
     202       44464 :                 shuffle_up(q, k);
     203             :         }
     204       61490 : }
     205             : 
     206      442238 : _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
     207             :         struct prioq_item *i;
     208             : 
     209      442238 :         assert(q);
     210             : 
     211      442238 :         if (q->n_items <= 0)
     212           2 :                 return NULL;
     213             : 
     214      442236 :         if (idx) {
     215      438141 :                 if (*idx == PRIOQ_IDX_NULL ||
     216      438141 :                     *idx >= q->n_items)
     217        4097 :                         return NULL;
     218             : 
     219      434044 :                 i = q->items + *idx;
     220      434044 :                 if (i->data != data)
     221        4094 :                         return NULL;
     222             : 
     223      429950 :                 return i;
     224             :         } else {
     225     8390655 :                 for (i = q->items; i < q->items + q->n_items; i++)
     226     8386560 :                         if (i->data == data)
     227           0 :                                 return i;
     228        4095 :                 return NULL;
     229             :         }
     230             : }
     231             : 
     232       66609 : int prioq_remove(Prioq *q, void *data, unsigned *idx) {
     233             :         struct prioq_item *i;
     234             : 
     235       66609 :         if (!q)
     236           0 :                 return 0;
     237             : 
     238       66609 :         i = find_item(q, data, idx);
     239       66609 :         if (!i)
     240       12288 :                 return 0;
     241             : 
     242       54321 :         remove_item(q, i);
     243       54321 :         return 1;
     244             : }
     245             : 
     246      375629 : int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
     247             :         struct prioq_item *i;
     248             :         unsigned k;
     249             : 
     250      375629 :         assert(q);
     251             : 
     252      375629 :         i = find_item(q, data, idx);
     253      375629 :         if (!i)
     254           0 :                 return 0;
     255             : 
     256      375629 :         k = i - q->items;
     257      375629 :         k = shuffle_down(q, k);
     258      375629 :         shuffle_up(q, k);
     259      375629 :         return 1;
     260             : }
     261             : 
     262      843828 : void *prioq_peek_by_index(Prioq *q, unsigned idx) {
     263      843828 :         if (!q)
     264      306005 :                 return NULL;
     265             : 
     266      537823 :         if (idx >= q->n_items)
     267       16648 :                 return NULL;
     268             : 
     269      521175 :         return q->items[idx].data;
     270             : }
     271             : 
     272        7169 : void *prioq_pop(Prioq *q) {
     273             :         void *data;
     274             : 
     275        7169 :         if (!q)
     276           0 :                 return NULL;
     277             : 
     278        7169 :         if (q->n_items <= 0)
     279           0 :                 return NULL;
     280             : 
     281        7169 :         data = q->items[0].data;
     282        7169 :         remove_item(q, q->items);
     283        7169 :         return data;
     284             : }
     285             : 
     286       11264 : unsigned prioq_size(Prioq *q) {
     287             : 
     288       11264 :         if (!q)
     289           0 :                 return 0;
     290             : 
     291       11264 :         return q->n_items;
     292             : }
     293             : 
     294          78 : bool prioq_isempty(Prioq *q) {
     295             : 
     296          78 :         if (!q)
     297           0 :                 return true;
     298             : 
     299          78 :         return q->n_items <= 0;
     300             : }

Generated by: LCOV version 1.14