priority_queue.c 14 KB

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