LCOV - code coverage report
Current view: top level - aws-c-common/source - priority_queue.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 81 345 23.5 %
Date: 2021-04-23 16:28:21 Functions: 8 19 42.1 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0.
       4             :  */
       5             : 
       6             : #include <aws/common/priority_queue.h>
       7             : 
       8             : #include <string.h>
       9             : 
      10           0 : #define PARENT_OF(index) (((index)&1) ? (index) >> 1 : (index) > 1 ? ((index)-2) >> 1 : 0)
      11           0 : #define LEFT_OF(index) (((index) << 1) + 1)
      12           0 : #define RIGHT_OF(index) (((index) << 1) + 2)
      13             : 
      14           0 : static void s_swap(struct aws_priority_queue *queue, size_t a, size_t b) {
      15           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
      16           0 :     AWS_PRECONDITION(a < queue->container.length);
      17           0 :     AWS_PRECONDITION(b < queue->container.length);
      18           0 :     AWS_PRECONDITION(aws_priority_queue_backpointer_index_valid(queue, a));
      19           0 :     AWS_PRECONDITION(aws_priority_queue_backpointer_index_valid(queue, b));
      20           0 : 
      21           0 :     aws_array_list_swap(&queue->container, a, b);
      22           0 : 
      23           0 :     /* Invariant: If the backpointer array is initialized, we have enough room for all elements */
      24           0 :     if (!AWS_IS_ZEROED(queue->backpointers)) {
      25           0 :         AWS_ASSERT(queue->backpointers.length > a);
      26           0 :         AWS_ASSERT(queue->backpointers.length > b);
      27           0 : 
      28           0 :         struct aws_priority_queue_node **bp_a = &((struct aws_priority_queue_node **)queue->backpointers.data)[a];
      29           0 :         struct aws_priority_queue_node **bp_b = &((struct aws_priority_queue_node **)queue->backpointers.data)[b];
      30           0 : 
      31           0 :         struct aws_priority_queue_node *tmp = *bp_a;
      32           0 :         *bp_a = *bp_b;
      33           0 :         *bp_b = tmp;
      34           0 : 
      35           0 :         if (*bp_a) {
      36           0 :             (*bp_a)->current_index = a;
      37           0 :         }
      38           0 : 
      39           0 :         if (*bp_b) {
      40           0 :             (*bp_b)->current_index = b;
      41           0 :         }
      42           0 :     }
      43           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
      44           0 :     AWS_POSTCONDITION(aws_priority_queue_backpointer_index_valid(queue, a));
      45           0 :     AWS_POSTCONDITION(aws_priority_queue_backpointer_index_valid(queue, b));
      46           0 : }
      47             : 
      48             : /* Precondition: with the exception of the given root element, the container must be
      49             :  * in heap order */
      50           0 : static bool s_sift_down(struct aws_priority_queue *queue, size_t root) {
      51           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
      52           0 :     AWS_PRECONDITION(root < queue->container.length);
      53           0 : 
      54           0 :     bool did_move = false;
      55           0 : 
      56           0 :     size_t len = aws_array_list_length(&queue->container);
      57           0 : 
      58           0 :     while (LEFT_OF(root) < len) {
      59           0 :         size_t left = LEFT_OF(root);
      60           0 :         size_t right = RIGHT_OF(root);
      61           0 :         size_t first = root;
      62           0 :         void *first_item = NULL, *other_item = NULL;
      63           0 : 
      64           0 :         aws_array_list_get_at_ptr(&queue->container, &first_item, root);
      65           0 :         aws_array_list_get_at_ptr(&queue->container, &other_item, left);
      66           0 : 
      67           0 :         if (queue->pred(first_item, other_item) > 0) {
      68           0 :             first = left;
      69           0 :             first_item = other_item;
      70           0 :         }
      71           0 : 
      72           0 :         if (right < len) {
      73           0 :             aws_array_list_get_at_ptr(&queue->container, &other_item, right);
      74           0 : 
      75           0 :             /* choose the larger/smaller of the two in case of a max/min heap
      76           0 :              * respectively */
      77           0 :             if (queue->pred(first_item, other_item) > 0) {
      78           0 :                 first = right;
      79           0 :                 first_item = other_item;
      80           0 :             }
      81           0 :         }
      82           0 : 
      83           0 :         if (first != root) {
      84           0 :             s_swap(queue, first, root);
      85           0 :             did_move = true;
      86           0 :             root = first;
      87           0 :         } else {
      88           0 :             break;
      89           0 :         }
      90           0 :     }
      91           0 : 
      92           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
      93           0 :     return did_move;
      94           0 : }
      95             : 
      96             : /* Precondition: Elements prior to the specified index must be in heap order. */
      97           0 : static bool s_sift_up(struct aws_priority_queue *queue, size_t index) {
      98           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
      99           0 :     AWS_PRECONDITION(index < queue->container.length);
     100           0 : 
     101           0 :     bool did_move = false;
     102           0 : 
     103           0 :     void *parent_item, *child_item;
     104           0 :     size_t parent = PARENT_OF(index);
     105           0 :     while (index) {
     106           0 :         /*
     107           0 :          * These get_ats are guaranteed to be successful; if they are not, we have
     108           0 :          * serious state corruption, so just abort.
     109           0 :          */
     110           0 : 
     111           0 :         if (aws_array_list_get_at_ptr(&queue->container, &parent_item, parent) ||
     112           0 :             aws_array_list_get_at_ptr(&queue->container, &child_item, index)) {
     113           0 :             abort();
     114           0 :         }
     115           0 : 
     116           0 :         if (queue->pred(parent_item, child_item) > 0) {
     117           0 :             s_swap(queue, index, parent);
     118           0 :             did_move = true;
     119           0 :             index = parent;
     120           0 :             parent = PARENT_OF(index);
     121           0 :         } else {
     122           0 :             break;
     123           0 :         }
     124           0 :     }
     125           0 : 
     126           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     127           0 :     return did_move;
     128           0 : }
     129             : 
     130             : /*
     131             :  * Precondition: With the exception of the given index, the heap condition holds for all elements.
     132             :  * In particular, the parent of the current index is a predecessor of all children of the current index.
     133             :  */
     134           0 : static void s_sift_either(struct aws_priority_queue *queue, size_t index) {
     135           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     136           0 :     AWS_PRECONDITION(index < queue->container.length);
     137           0 : 
     138           0 :     if (!index || !s_sift_up(queue, index)) {
     139           0 :         s_sift_down(queue, index);
     140           0 :     }
     141           0 : 
     142           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     143           0 : }
     144             : 
     145             : int aws_priority_queue_init_dynamic(
     146             :     struct aws_priority_queue *queue,
     147             :     struct aws_allocator *alloc,
     148             :     size_t default_size,
     149             :     size_t item_size,
     150         792 :     aws_priority_queue_compare_fn *pred) {
     151         792 : 
     152         792 :     AWS_FATAL_PRECONDITION(queue != NULL);
     153         792 :     AWS_FATAL_PRECONDITION(alloc != NULL);
     154         792 :     AWS_FATAL_PRECONDITION(item_size > 0);
     155         792 : 
     156         792 :     queue->pred = pred;
     157         792 :     AWS_ZERO_STRUCT(queue->backpointers);
     158         792 : 
     159         792 :     int ret = aws_array_list_init_dynamic(&queue->container, alloc, default_size, item_size);
     160         792 :     if (ret == AWS_OP_SUCCESS) {
     161         792 :         AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     162         792 :     } else {
     163           0 :         AWS_POSTCONDITION(AWS_IS_ZEROED(queue->container));
     164           0 :         AWS_POSTCONDITION(AWS_IS_ZEROED(queue->backpointers));
     165           0 :     }
     166         792 :     return ret;
     167         792 : }
     168             : 
     169             : void aws_priority_queue_init_static(
     170             :     struct aws_priority_queue *queue,
     171             :     void *heap,
     172             :     size_t item_count,
     173             :     size_t item_size,
     174       19671 :     aws_priority_queue_compare_fn *pred) {
     175       19671 : 
     176       19671 :     AWS_FATAL_PRECONDITION(queue != NULL);
     177       19671 :     AWS_FATAL_PRECONDITION(heap != NULL);
     178       19671 :     AWS_FATAL_PRECONDITION(item_count > 0);
     179       19671 :     AWS_FATAL_PRECONDITION(item_size > 0);
     180       19671 : 
     181       19671 :     queue->pred = pred;
     182       19671 :     AWS_ZERO_STRUCT(queue->backpointers);
     183       19671 : 
     184       19671 :     aws_array_list_init_static(&queue->container, heap, item_count, item_size);
     185       19671 : 
     186       19671 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     187       19671 : }
     188             : 
     189           0 : bool aws_priority_queue_backpointer_index_valid(const struct aws_priority_queue *const queue, size_t index) {
     190           0 :     if (AWS_IS_ZEROED(queue->backpointers)) {
     191           0 :         return true;
     192           0 :     }
     193           0 :     if (index < queue->backpointers.length) {
     194           0 :         struct aws_priority_queue_node *node = ((struct aws_priority_queue_node **)queue->backpointers.data)[index];
     195           0 :         return (node == NULL) || AWS_MEM_IS_WRITABLE(node, sizeof(struct aws_priority_queue_node));
     196           0 :     }
     197           0 :     return false;
     198           0 : }
     199             : 
     200           0 : bool aws_priority_queue_backpointers_valid_deep(const struct aws_priority_queue *const queue) {
     201           0 :     if (!queue) {
     202           0 :         return false;
     203           0 :     }
     204           0 :     for (size_t i = 0; i < queue->backpointers.length; i++) {
     205           0 :         if (!aws_priority_queue_backpointer_index_valid(queue, i)) {
     206           0 :             return false;
     207           0 :         }
     208           0 :     }
     209           0 :     return true;
     210           0 : }
     211             : 
     212      368777 : bool aws_priority_queue_backpointers_valid(const struct aws_priority_queue *const queue) {
     213      368777 :     if (!queue) {
     214           0 :         return false;
     215           0 :     }
     216      368777 : 
     217      368777 :     /* Internal container validity */
     218      368777 :     bool backpointer_list_is_valid =
     219      368777 :         ((aws_array_list_is_valid(&queue->backpointers) && (queue->backpointers.current_size != 0) &&
     220      368777 :           (queue->backpointers.data != NULL)));
     221      368777 : 
     222      368777 :     /* Backpointer struct should either be zero or should be
     223      368777 :      * initialized to be at most as long as the container, and having
     224      368777 :      * as elements potentially null pointers to
     225      368777 :      * aws_priority_queue_nodes */
     226      368777 :     bool backpointer_list_item_size = queue->backpointers.item_size == sizeof(struct aws_priority_queue_node *);
     227      368777 :     bool lists_equal_lengths = queue->backpointers.length == queue->container.length;
     228      368777 :     bool backpointers_non_zero_current_size = queue->backpointers.current_size > 0;
     229      368777 : 
     230      368777 :     /* This check must be guarded, as it is not efficient, neither
     231      368777 :      * when running tests nor CBMC */
     232             : #if (AWS_DEEP_CHECKS == 1)
     233             :     bool backpointers_valid_deep = aws_priority_queue_backpointers_valid_deep(queue);
     234             : #else
     235             :     bool backpointers_valid_deep = true;
     236      368777 : #endif
     237      368777 :     bool backpointers_zero =
     238      368777 :         (queue->backpointers.current_size == 0 && queue->backpointers.length == 0 && queue->backpointers.data == NULL);
     239      368777 :     bool backpointer_struct_is_valid =
     240      368777 :         backpointers_zero || (backpointer_list_item_size && lists_equal_lengths && backpointers_non_zero_current_size &&
     241      283868 :                               backpointers_valid_deep);
     242      368777 : 
     243      368777 :     return ((backpointer_list_is_valid && backpointer_struct_is_valid) || AWS_IS_ZEROED(queue->backpointers));
     244      368777 : }
     245             : 
     246      368777 : bool aws_priority_queue_is_valid(const struct aws_priority_queue *const queue) {
     247      368777 :     /* Pointer validity checks */
     248      368777 :     if (!queue) {
     249           0 :         return false;
     250           0 :     }
     251      368777 :     bool pred_is_valid = (queue->pred != NULL);
     252      368777 :     bool container_is_valid = aws_array_list_is_valid(&queue->container);
     253      368777 : 
     254      368777 :     bool backpointers_valid = aws_priority_queue_backpointers_valid(queue);
     255      368777 :     return pred_is_valid && container_is_valid && backpointers_valid;
     256      368777 : }
     257             : 
     258         317 : void aws_priority_queue_clean_up(struct aws_priority_queue *queue) {
     259         317 :     aws_array_list_clean_up(&queue->container);
     260         317 :     if (!AWS_IS_ZEROED(queue->backpointers)) {
     261         317 :         aws_array_list_clean_up(&queue->backpointers);
     262         317 :     }
     263         317 : }
     264             : 
     265           0 : int aws_priority_queue_push(struct aws_priority_queue *queue, void *item) {
     266           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     267           0 :     AWS_PRECONDITION(item && AWS_MEM_IS_READABLE(item, queue->container.item_size));
     268           0 :     int rval = aws_priority_queue_push_ref(queue, item, NULL);
     269           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     270           0 :     return rval;
     271           0 : }
     272             : 
     273             : int aws_priority_queue_push_ref(
     274             :     struct aws_priority_queue *queue,
     275             :     void *item,
     276           0 :     struct aws_priority_queue_node *backpointer) {
     277           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     278           0 :     AWS_PRECONDITION(item && AWS_MEM_IS_READABLE(item, queue->container.item_size));
     279           0 : 
     280           0 :     int err = aws_array_list_push_back(&queue->container, item);
     281           0 :     if (err) {
     282           0 :         AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     283           0 :         return err;
     284           0 :     }
     285           0 :     size_t index = aws_array_list_length(&queue->container) - 1;
     286           0 : 
     287           0 :     if (backpointer && !queue->backpointers.alloc) {
     288           0 :         if (!queue->container.alloc) {
     289           0 :             aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION);
     290           0 :             goto backpointer_update_failed;
     291           0 :         }
     292           0 : 
     293           0 :         if (aws_array_list_init_dynamic(
     294           0 :                 &queue->backpointers, queue->container.alloc, index + 1, sizeof(struct aws_priority_queue_node *))) {
     295           0 :             goto backpointer_update_failed;
     296           0 :         }
     297           0 : 
     298           0 :         /* When we initialize the backpointers array we need to zero out all existing entries */
     299           0 :         memset(queue->backpointers.data, 0, queue->backpointers.current_size);
     300           0 :     }
     301           0 : 
     302           0 :     /*
     303           0 :      * Once we have any backpointers, we want to make sure we always have room in the backpointers array
     304           0 :      * for all elements; otherwise, sift_down gets complicated if it runs out of memory when sifting an
     305           0 :      * element with a backpointer down in the array.
     306           0 :      */
     307           0 :     if (!AWS_IS_ZEROED(queue->backpointers)) {
     308           0 :         if (aws_array_list_set_at(&queue->backpointers, &backpointer, index)) {
     309           0 :             goto backpointer_update_failed;
     310           0 :         }
     311           0 :     }
     312           0 : 
     313           0 :     if (backpointer) {
     314           0 :         backpointer->current_index = index;
     315           0 :     }
     316           0 : 
     317           0 :     s_sift_up(queue, aws_array_list_length(&queue->container) - 1);
     318           0 : 
     319           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     320           0 :     return AWS_OP_SUCCESS;
     321           0 : 
     322           0 : backpointer_update_failed:
     323           0 :     /* Failed to initialize or grow the backpointer array, back out the node addition */
     324           0 :     aws_array_list_pop_back(&queue->container);
     325           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     326           0 :     return AWS_OP_ERR;
     327           0 : }
     328             : 
     329           0 : static int s_remove_node(struct aws_priority_queue *queue, void *item, size_t item_index) {
     330           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     331           0 :     AWS_PRECONDITION(item && AWS_MEM_IS_WRITABLE(item, queue->container.item_size));
     332           0 :     if (aws_array_list_get_at(&queue->container, item, item_index)) {
     333           0 :         /* shouldn't happen, but if it does we've already raised an error... */
     334           0 :         AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     335           0 :         return AWS_OP_ERR;
     336           0 :     }
     337           0 : 
     338           0 :     size_t swap_with = aws_array_list_length(&queue->container) - 1;
     339           0 :     struct aws_priority_queue_node *backpointer = NULL;
     340           0 : 
     341           0 :     if (item_index != swap_with) {
     342           0 :         s_swap(queue, item_index, swap_with);
     343           0 :     }
     344           0 : 
     345           0 :     aws_array_list_pop_back(&queue->container);
     346           0 : 
     347           0 :     if (!AWS_IS_ZEROED(queue->backpointers)) {
     348           0 :         aws_array_list_get_at(&queue->backpointers, &backpointer, swap_with);
     349           0 :         if (backpointer) {
     350           0 :             backpointer->current_index = SIZE_MAX;
     351           0 :         }
     352           0 :         aws_array_list_pop_back(&queue->backpointers);
     353           0 :     }
     354           0 : 
     355           0 :     if (item_index != swap_with) {
     356           0 :         s_sift_either(queue, item_index);
     357           0 :     }
     358           0 : 
     359           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     360           0 :     return AWS_OP_SUCCESS;
     361           0 : }
     362             : 
     363             : int aws_priority_queue_remove(
     364             :     struct aws_priority_queue *queue,
     365             :     void *item,
     366           0 :     const struct aws_priority_queue_node *node) {
     367           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     368           0 :     AWS_PRECONDITION(item && AWS_MEM_IS_WRITABLE(item, queue->container.item_size));
     369           0 :     AWS_PRECONDITION(node && AWS_MEM_IS_READABLE(node, sizeof(struct aws_priority_queue_node)));
     370           0 :     AWS_ERROR_PRECONDITION(
     371           0 :         node->current_index < aws_array_list_length(&queue->container), AWS_ERROR_PRIORITY_QUEUE_BAD_NODE);
     372           0 :     AWS_ERROR_PRECONDITION(queue->backpointers.data, AWS_ERROR_PRIORITY_QUEUE_BAD_NODE);
     373           0 : 
     374           0 :     int rval = s_remove_node(queue, item, node->current_index);
     375           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     376           0 :     return rval;
     377           0 : }
     378             : 
     379           0 : int aws_priority_queue_pop(struct aws_priority_queue *queue, void *item) {
     380           0 :     AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
     381           0 :     AWS_PRECONDITION(item && AWS_MEM_IS_WRITABLE(item, queue->container.item_size));
     382           0 :     AWS_ERROR_PRECONDITION(aws_array_list_length(&queue->container) != 0, AWS_ERROR_PRIORITY_QUEUE_EMPTY);
     383           0 : 
     384           0 :     int rval = s_remove_node(queue, item, 0);
     385           0 :     AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
     386           0 :     return rval;
     387           0 : }
     388             : 
     389        6618 : int aws_priority_queue_top(const struct aws_priority_queue *queue, void **item) {
     390        6618 :     AWS_ERROR_PRECONDITION(aws_array_list_length(&queue->container) != 0, AWS_ERROR_PRIORITY_QUEUE_EMPTY);
     391        6618 :     return aws_array_list_get_at_ptr(&queue->container, item, 0);
     392        6618 : }
     393             : 
     394        3892 : size_t aws_priority_queue_size(const struct aws_priority_queue *queue) {
     395        3892 :     return aws_array_list_length(&queue->container);
     396        3892 : }
     397             : 
     398        2363 : size_t aws_priority_queue_capacity(const struct aws_priority_queue *queue) {
     399        2363 :     return aws_array_list_capacity(&queue->container);
     400        2363 : }

Generated by: LCOV version 1.13